You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The continuations feature of the Espresso VM allows you to control the program stack. When a continuation is _suspended_
4
-
the stack is unwound and copied onto the heap as ordinary Java objects. When a continuation is _resumed_ those objects
5
-
are put back onto the stack, along with all the needed metadata to resume execution at the pause point. The heap objects
6
-
can be serialized to resume execution in a different JVM running the same code (e.g. after a restart).
8
+
# Continuation API
9
+
10
+
The [Continuation API](https://mvnrepository.com/artifact/org.graalvm.espresso/continuations) enables you to control the program stack.
11
+
When a continuation is _suspended_, the stack is unwound and copied onto the heap as ordinary Java objects.
12
+
When a continuation is _resumed_, those objects are put back onto the stack, along with all the needed metadata to resume execution at the pause point.
13
+
The heap objects can be serialized to resume execution in a different JVM running the same code (for example, after a restart).
7
14
8
15
## Usage
9
16
10
-
See the JavaDoc of the `org.graalvm.continuations` package, and make sure to add the `continuations.jar` in the Espresso
11
-
distribution to your classpath when compiling (but not at runtime).
17
+
Add the `continuations.jar` to your classpath at compilation time (it will be automatically provided at runtime).
18
+
The continuation feature is experimental and needs to be explicitly enabled by using these options: `--experimental-options --java.Continuum=true`.
12
19
13
-
Currently, only the Espresso VM supports the continuations feature. Since it is still experimental, the option needs to
14
-
be enabled by using the flags `--experimental-options --java.Continuum=true`.
20
+
See an [example usage](serialization.md) of the Continuation API with serialization.
15
21
16
-
### High level
22
+
### High Level
17
23
18
24
If you can model your use case as code that emits (or _yields_) a stream of objects, you can use the `Generator<T>`
19
-
class. This provides something similar to Python's generators with a convenient API. Just subclass it,
20
-
implement `generate` and call `emit` from inside it.
25
+
class. This provides something similar to Python's generators with a convenient API:
26
+
subclass it, implement `generate`, and call `emit` from inside it.
27
+
28
+
[See an example of using the Generator API](generators.md).
21
29
22
-
### Low level
30
+
### Low Level
23
31
24
32
You create a new `Continuation` by passing the constructor an object that implements the functional
25
33
interface `ContinuationEntryPoint` (which can be a lambda). That object's `start` method receives
@@ -29,10 +37,10 @@ unwound and stored inside the `Continuation` object. You can then call `resume()
29
37
time or to restart from the last suspend point.
30
38
31
39
Continuations are single-threaded constructs. There are no second threads involved, and the `resume()` method blocks
32
-
until the continuation either finishes successfully, throws an exception or calls suspend.`isResumable()` can be called
33
-
to determine if the continuation can be resumed (if the continuation has been freshly created or it has been previously
34
-
suspended), and `isCompleted()`can be called to determine if the continuation has completed (either by returning
35
-
normally, or if an exception escaped).
40
+
until the continuation either finishes successfully, throws an exception or calls suspend.
41
+
You can use `isResumable()`to check if the continuation can be resumed (for example, if the continuation has been
42
+
freshly created or it has been previously suspended), and `isCompleted()` to verify whether the continuation has completed
43
+
(either by returning normally, or if an exception escaped).
36
44
37
45
`Continuation` implements `Serializable` and can serialize to a backwards compatible format. Because frames can point to
38
46
anything in their parameters and local variables, the class `ContinuationSerializable` provides static
@@ -56,7 +64,7 @@ prevent a VM crash. Examples of these checks are:
56
64
Deserializing a continuation supplied by an attacker will allow complete takeover of the JVM. Only resume continuations
57
65
you persisted yourself!
58
66
59
-
## Use cases
67
+
## Use Cases
60
68
61
69
Serializing a continuation makes it _multi-shot_, meaning you can restart a continuation more than once and thus redo
62
70
the same computation with different inputs. This ability to explore "parallel worlds" opens up many interesting use
@@ -83,7 +91,7 @@ cases.
83
91
to migrate units of work.
84
92
-**Parsing**: Implementing non-deterministic parsers or interpreters where multiple potential parsing paths are
85
93
explored simultaneously.
86
-
-**Custom Control Structures**: Creating new control structures (like loops, exception handling) that are not natively
94
+
-**Custom Control Structures**: Creating new control structures (for example, loops, exception handling) that are not natively
87
95
supported in the programming language.
88
96
-**Time-travel Debugging**: Capturing continuations at various points in a program to enable "stepping back" in time
89
97
during debugging sessions.
@@ -94,22 +102,23 @@ cases.
94
102
95
103
CPUs attempt to guess the direction of a branch when the data it depends on hasn't arrived yet. This is helpful because
96
104
memory is slow. The same trick can be done at much higher levels using continuations when reading data from slow data
97
-
sources like a far away server. If you have a computation where CPU intensive work is interleaved with long blocking
105
+
sources such as a far away server. If you have a computation where CPU intensive work is interleaved with long blocking
98
106
periods, this can speed things up.
99
107
100
-
When a continuation performs a slow operation that yields a value (e.g. an RPC), we can suspend, serialize, dispatch the
101
-
request and then instead of waiting for the result and scheduling other work - as would be standard in a
102
-
continuation-based async threads implementation like Loom - we can instead pick one or more values that we think the RPC
103
-
_might_ return. Then we deserialize the continuation again for each value we want to try and resume it. This forks
104
-
execution into multiple parallel universes. The new paths can then fork again, forming a tree of possibilities. If the
105
-
continuation tries to do something that isn't controlled by the surrounding framework and can't be undone (a side
106
-
effect), it suspends at that point and doesn't continue (this is as far as we can speculate).
108
+
When a continuation performs a slow operation that yields a value (for example, an RPC), you can suspend, serialize, dispatch the request.
109
+
Instead of waiting for the result and scheduling other work—as would be standard in continuation-based async threads implementations such as [Project Loom](https://wiki.openjdk.org/display/loom/Main)—you
110
+
can instead pick one or more values that we think the RPC _might_ return.
111
+
Then the continuation is deserialized for each value, resuming execution separately for each possibility.
112
+
This approach forks the execution into multiple parallel paths. The new paths can then fork again, forming a tree of possibilities.
113
+
114
+
If the continuation encounters an operation that is not controlled by the surrounding framework and cannot be undone (for example, a side
115
+
effect), it suspends at that point and doesn't continue speculatively.
107
116
108
-
As results arrive from the remote server we can steadily resolve our way down the tree discarding all the serialized
109
-
continuations that exist in worlds that weren't taken. Eventually the final result is received and the continuation can
110
-
finish, or continue past a side effecting point.
117
+
When results arrive from the remote server, the speculative tree of serialized continuations can be resolved incrementally.
118
+
Serialized continuations corresponding to paths that are no longer valid are discarded.
119
+
Once the final result is received, the continuation either completes execution or proceeds beyond a point where side effects occur.
111
120
112
-
If the server protocol allows results to be chained together (e.g.
121
+
If the server protocol allows results to be chained together (for example,
113
122
as [Cap'n'Proto RPC does](https://capnproto.org/rpc.html#time-travel-promise-pipelining),
114
123
or [FoundationDB](https://github.com/apple/foundationdb/wiki/Everything-about-GetMappedRange)), back-to-back speculative
115
124
calls can be transmitted to the server for local processing, avoiding roundtrips.
@@ -126,11 +135,11 @@ There are special situations in which a call to `suspend` may fail with `Illegal
126
135
127
136
Furthermore, there is currently no support for continuation-in-continuation.
128
137
129
-
## Internal implementation notes
138
+
## Internal Implementation Notes
130
139
131
140
*This section is only relevant for people working on Espresso itself.*
132
141
133
-
Continuations interact with the VM via private intrinsics registered on the `Continuation` and `ContinuationImpl`class.
142
+
Continuations interact with the VM through private intrinsics registered for the `Continuation` and `ContinuationImpl`classes.
134
143
135
144
A continuation starts by calling into the VM. Execution resurfaces in the guest world at the private `run` method of
136
145
`ContinuationImpl`, which then invokes the user's given entry point.
@@ -144,13 +153,18 @@ On resuming a `Continuation`, the entire call stack needs to be re-winded. This
144
153
than for regular calls, and there is one such call target per encountered resume `bci`.
145
154
146
155
These call targets take a single argument: the `HostFrameRecord` that was stored into the `Continuation`. Using this
147
-
record, we restore the frame for the current method, we unlink the current record from the rest (for GC purposes), and
148
-
we pass the rest of the records to the next method. This is all done in a special invoke node, `InvokeContinuableNode`.
156
+
record, the frame is restored for the current method, the current record is unlinked from the rest (for GC purposes), and
157
+
the rest of the records is passed to the next method. This is all done in a special invoke node, `InvokeContinuableNode`.
149
158
150
159
The separation of the call targets has two advantages:
151
160
152
161
- It does not interfere with regular calls.
153
162
- Resuming and suspending can be partial-evaluated, leading to fast suspend/resume cycles.
154
163
155
164
Serialization is done entirely in guest-side code, by having the `Continuation` class implement `Serializable`. The
156
-
format is designed to enable backwards-compatible evolution of the format.
165
+
format is designed to enable backwards-compatible evolution of the format.
166
+
167
+
### Further Reading
168
+
*[Serialization of Continuations](serialization.md)
0 commit comments