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
@@ -57,7 +65,7 @@ prevent a VM crash. Examples of these checks are:
57
65
Deserializing a continuation supplied by an attacker will allow complete takeover of the JVM. Only resume continuations
58
66
you persisted yourself!
59
67
60
-
## Use cases
68
+
## Use Cases
61
69
62
70
Serializing a continuation makes it _multi-shot_, meaning you can restart a continuation more than once and thus redo
63
71
the same computation with different inputs. This ability to explore "parallel worlds" opens up many interesting use
@@ -84,7 +92,7 @@ cases.
84
92
to migrate units of work.
85
93
-**Parsing**: Implementing non-deterministic parsers or interpreters where multiple potential parsing paths are
86
94
explored simultaneously.
87
-
-**Custom Control Structures**: Creating new control structures (like loops, exception handling) that are not natively
95
+
-**Custom Control Structures**: Creating new control structures (for example, loops, exception handling) that are not natively
88
96
supported in the programming language.
89
97
-**Time-travel Debugging**: Capturing continuations at various points in a program to enable "stepping back" in time
90
98
during debugging sessions.
@@ -95,22 +103,23 @@ cases.
95
103
96
104
CPUs attempt to guess the direction of a branch when the data it depends on hasn't arrived yet. This is helpful because
97
105
memory is slow. The same trick can be done at much higher levels using continuations when reading data from slow data
98
-
sources like a far away server. If you have a computation where CPU intensive work is interleaved with long blocking
106
+
sources such as a far away server. If you have a computation where CPU intensive work is interleaved with long blocking
99
107
periods, this can speed things up.
100
108
101
-
When a continuation performs a slow operation that yields a value (e.g. an RPC), we can suspend, serialize, dispatch the
102
-
request and then instead of waiting for the result and scheduling other work - as would be standard in a
103
-
continuation-based async threads implementation like Loom - we can instead pick one or more values that we think the RPC
104
-
_might_ return. Then we deserialize the continuation again for each value we want to try and resume it. This forks
105
-
execution into multiple parallel universes. The new paths can then fork again, forming a tree of possibilities. If the
106
-
continuation tries to do something that isn't controlled by the surrounding framework and can't be undone (a side
107
-
effect), it suspends at that point and doesn't continue (this is as far as we can speculate).
109
+
When a continuation performs a slow operation that yields a value (for example, an RPC), you can suspend, serialize, dispatch the request.
110
+
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
111
+
can instead pick one or more values that we think the RPC _might_ return.
112
+
Then the continuation is deserialized for each value, resuming execution separately for each possibility.
113
+
This approach forks the execution into multiple parallel paths. The new paths can then fork again, forming a tree of possibilities.
114
+
115
+
If the continuation encounters an operation that is not controlled by the surrounding framework and cannot be undone (for example, a side
116
+
effect), it suspends at that point and doesn't continue speculatively.
108
117
109
-
As results arrive from the remote server we can steadily resolve our way down the tree discarding all the serialized
110
-
continuations that exist in worlds that weren't taken. Eventually the final result is received and the continuation can
111
-
finish, or continue past a side effecting point.
118
+
When results arrive from the remote server, the speculative tree of serialized continuations can be resolved incrementally.
119
+
Serialized continuations corresponding to paths that are no longer valid are discarded.
120
+
Once the final result is received, the continuation either completes execution or proceeds beyond a point where side effects occur.
112
121
113
-
If the server protocol allows results to be chained together (e.g.
122
+
If the server protocol allows results to be chained together (for example,
114
123
as [Cap'n'Proto RPC does](https://capnproto.org/rpc.html#time-travel-promise-pipelining),
115
124
or [FoundationDB](https://github.com/apple/foundationdb/wiki/Everything-about-GetMappedRange)), back-to-back speculative
116
125
calls can be transmitted to the server for local processing, avoiding roundtrips.
@@ -127,11 +136,11 @@ There are special situations in which a call to `suspend` may fail with `Illegal
127
136
128
137
Furthermore, there is currently no support for continuation-in-continuation.
129
138
130
-
## Internal implementation notes
139
+
## Internal Implementation Notes
131
140
132
141
*This section is only relevant for people working on Espresso itself.*
133
142
134
-
Continuations interact with the VM via private intrinsics registered on the `Continuation` and `ContinuationImpl`class.
143
+
Continuations interact with the VM through private intrinsics registered for the `Continuation` and `ContinuationImpl`classes.
135
144
136
145
A continuation starts by calling into the VM. Execution resurfaces in the guest world at the private `run` method of
137
146
`ContinuationImpl`, which then invokes the user's given entry point.
@@ -145,13 +154,18 @@ On resuming a `Continuation`, the entire call stack needs to be re-winded. This
145
154
than for regular calls, and there is one such call target per encountered resume `bci`.
146
155
147
156
These call targets take a single argument: the `HostFrameRecord` that was stored into the `Continuation`. Using this
148
-
record, we restore the frame for the current method, we unlink the current record from the rest (for GC purposes), and
149
-
we pass the rest of the records to the next method. This is all done in a special invoke node, `InvokeContinuableNode`.
157
+
record, the frame is restored for the current method, the current record is unlinked from the rest (for GC purposes), and
158
+
the rest of the records is passed to the next method. This is all done in a special invoke node, `InvokeContinuableNode`.
150
159
151
160
The separation of the call targets has two advantages:
152
161
153
162
- It does not interfere with regular calls.
154
163
- Resuming and suspending can be partial-evaluated, leading to fast suspend/resume cycles.
155
164
156
165
Serialization is done entirely in guest-side code, by having the `Continuation` class implement `Serializable`. The
157
-
format is designed to enable backwards-compatible evolution of the format.
166
+
format is designed to enable backwards-compatible evolution of the format.
167
+
168
+
### Further Reading
169
+
*[Serialization of Continuations](serialization.md)
0 commit comments