-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
) Add opt-in support for efficient abstraction of asynchronous code using `async*`/`await*` to delineate possible commits/context switches. Fixes #1482 and addresses https://forum.dfinity.org/t/canister-output-message-queue-limits-and-ic-management-canister-throttling-limits/15972/22. Syntax: ``` <type ::= ... async* <typ> delayed, asynchronous computation <exp> ::= ... async* <block-or-exp> delay an asynchronous computation await* <block-or-exp> await a delayed computation (only in async) ``` This is another take on the design, avoid some of the pitfalls (but also advantages) of #3573 which proposed an eager `do async <block_or_exp>` but no new types. ## Async* types `async* <typ>` specifies a delayed, asynchronous computation producing a value of type `<typ>`. Computation types typically appear as the result type of a `local` function that produces an `await*`-able value. (They cannot be used as the return types of `shared` functions.) ## Async* The async expression `async* <block-or-exp>` has type `async* T` provided: - `<block-or-exp>` has type `T`; - `T` is shared. Any control-flow label in scope for `async* <block-or-exp>` is not in scope for `<block-or-exp>`. However, `<block-or-exp>` may declare and use its own, local, labels. The implicit return type in `<block-or-exp>` is `T`. That is, the return expression, `<exp0>`, (implicit or explicit) to any enclosed `return <exp0>?` expression, must have type `T`. Evaluation of `async* <block-or-exp>` produces a delayed computation to evaluate `<block-or-exp>`. It immediately returns a value of type `async* T`. The delayed computation can be executed using `await*`, producing one evaluation of the computation `<block-or-exp>`. ### Danger Note that `async <block-or-exp>` has the effect of scheduling a single asynchronous computation of `<exp>`, regardless of whether its result, a future, is consumed with an `await`. Moreover, each additional consumption by an `await` just returns the previous result, without repeating the computation. In comparison, `async* <block-or_exp>`, has *no effect* until its value is consumed by an `await*`. Moreover, each additional consumption by an `await*` will trigger a new evaluation of `<block-or-exp>`. Be careful of this distinction, and other differences, when refactoring code. ### Note: The `async*` and corresponding `await*` constructs are useful for efficiently abstracting asynchronous code into re-useable functions. In comparison, calling a local function that returns a proper `async` type requires committing state and suspending execution with each `await` of its result, which can be undesirable. ## Await* The `await*` expression `await* <exp>` has type `T` provided: - `<exp>` has type `async* T`, - `T` is shared, - the `await*` is explicitly enclosed by an `async`-expression or appears in the body of a `shared` function. Expression `await <exp>` evaluates `<exp>` to a result `r`. If `r` is `trap`, evaluation returns `trap`. Otherwise `r` is a delayed computation `<block-or-exp>`. The evaluation of `await* <exp>` proceeds with the evaluation of `<block-or-exp>`, executing the delayed computation. ### Danger During the evaluation of `<block-or-exp>`, the state of the enclosing actor may change due to concurrent processing of other incoming actor messages. It is the programmer’s responsibility to guard against non-synchronized state changes. ### Note Unlike `await`, which, regardless of the dynamic status of the future, ensures that all tentative state changes and message sends prior to the `await` are committed and irrevocable, `await*` does not, in itself, commit any state changes, nor does it suspend computation. Instead, evaluation proceeds immediately according to `<block-or-exp>` (the value of `<exp>`), committing state and suspending execution whenever `<block-or-exp>` does (but not otherwise). ### Note Evaluation of a delayed `async*` block is synchronous while possible, switching to asynchronous when necessary due to a proper `await`. Using `await*` signals that the computation *may* commit state and suspend execution during the evaluation of `<block-or-exp>`, that is, that evaluation of `<block-or-exp>` may perform zero or more proper `await`s and may be interleaved with the execution of other, concurrent messages. ## TODO: Future: - [ ] generics and flattening - rule out generics for now? - [ ] relax shared content type for `async*` types? One annoying thing is that we cannot make actor class instantiation yet more efficient without returning an `async*`, breaking code. Although I guess users could opt in to that if wanted (by giving an `async*` return type). That's a drawback compared to the previous `do async/await` approach. But them's the breaks.
- Loading branch information
Showing
95 changed files
with
1,203 additions
and
349 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.