Skip to content

Commit a358a88

Browse files
committed
[GR-54518] Polish Continuations API
PullRequest: graal/18046
2 parents d61aea6 + 5cc8263 commit a358a88

18 files changed

+1728
-949
lines changed

espresso/docs/continuations.md

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ can be serialized to resume execution in a different JVM running the same code (
1010
See the JavaDoc of the `org.graalvm.continuations` package, and make sure to add the `continuations.jar` in the Espresso
1111
distribution to your classpath when compiling (but not at runtime).
1212

13-
Currently, only the Espresso VM supports the continuations feature.
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`.
1415

1516
### High level
1617

@@ -21,36 +22,36 @@ implement `generate` and call `emit` from inside it.
2122
### Low level
2223

2324
You create a new `Continuation` by passing the constructor an object that implements the functional
24-
interface `Continuation.EntryPoint` (which can be a lambda). That object's `start` method receives
25-
a `Continuation.SuspendCapability` that lets you trigger a suspend. You can do that from _any_ depth in the stack as
26-
long as the code was invoked via the entry point, and all the frames between the call to `suspend` and `start` will be
25+
interface `ContinuationEntryPoint` (which can be a lambda). That object's `start` method receives
26+
a `SuspendCapability` that lets you trigger suspension. You can do that from _any_ depth in the stack as
27+
long as the code was invoked via the entry point, and all the frames between the call to `suspend` and `resume` will be
2728
unwound and stored inside the `Continuation` object. You can then call `resume()` on it to kick it off for the first
2829
time or to restart from the last suspend point.
2930

3031
Continuations are single-threaded constructs. There are no second threads involved, and the `resume()` method blocks
31-
until the continuation either finishes successfully, throws an exception or calls suspend. The difference can be seen in
32-
the result of the `getState()` method.
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).
3336

34-
`Continuation` implements `Externalizable` and can serialize to a backwards compatible format (not guaranteed until
35-
final release). Because frames can point to anything in their parameters and local variables, it must be persisted by a
36-
serialization engine that can handle arbitrary objects like [Kryo](https://github.com/EsotericSoftware/kryo/)
37-
or `ObjectOutputStream`.
37+
`Continuation` implements `Serializable` and can serialize to a backwards compatible format. Because frames can point to
38+
anything in their parameters and local variables, the class `ContinuationSerializable` provides static
39+
methods `readObjectExternal` and `writeObjectExternal` which may be used to coordinate serialization of
40+
continuation-related objects with a non-jdk serialization engine.
3841

3942
## Security
4043

4144
Continuations that have **not** been _materialized_ are safe, as the frame record is kept internal to the VM.
4245

4346
Materializing a continuation refers to making the record visible to Java code, through the
44-
private `Continuation.stackFrameHead` field.
45-
46-
Currently, the only path for materialization is through serialization.
47+
private `ContinuationImpl.stackFrameHead` field. Currently, the only path for materialization is through serialization.
4748

4849
When restoring from a materialized frame (_dematerialization_), only minimal checks are performed by the VM, and only to
4950
prevent a VM crash. Examples of these checks are:
5051

5152
- Ensures that resume only happens on `invoke` bytecodes.
5253
- Ensures that the recorded frame data is consistent with what was computed by the bytecode verifier.
53-
- Ensures that the last frame in the record is `Continuation.suspend`.
54+
- Ensures that the last frame in the record is `ContinuationImpl.suspend`.
5455

5556
Deserializing a continuation supplied by an attacker will allow complete takeover of the JVM. Only resume continuations
5657
you persisted yourself!
@@ -129,15 +130,15 @@ Furthermore, there is currently no support for continuation-in-continuation.
129130

130131
*This section is only relevant for people working on Espresso itself.*
131132

132-
Continuations interact with the VM via private intrinsics registered on the `Continuation` class.
133+
Continuations interact with the VM via private intrinsics registered on the `Continuation` and `ContinuationImpl` class.
133134

134135
A continuation starts by calling into the VM. Execution resurfaces in the guest world at the private `run` method of
135-
`Continuation`, which then invokes the user's given entry point.
136+
`ContinuationImpl`, which then invokes the user's given entry point.
136137

137138
Suspending throws a host-side exception that is caught by the `BytecodeNode` interpreter loop. The stack frame is copied
138139
into a new host-side object called a `HostFrameRecord` (HFR). The exception is then rethrown. The HFRs are chained
139-
together in a linked list. Once execution reaches the private `run` method of `Continuation` the HFR list is attached
140-
into a hidden field of `Continuation`. Control is then returned to the guest.
140+
together in a linked list. Once execution reaches the private `run` method of `ContinuationImpl` the HFR list is
141+
attached into a hidden field of `ContinuationImpl`. Control is then returned to the guest.
141142

142143
On resuming a `Continuation`, the entire call stack needs to be re-winded. This happens through a different `CallTarget`
143144
than for regular calls, and there is one such call target per encountered resume `bci`.
@@ -149,7 +150,7 @@ we pass the rest of the records to the next method. This is all done in a specia
149150
The separation of the call targets has two advantages:
150151

151152
- It does not interfere with regular calls.
152-
- Resuming and suspending can be PE'd, leading to fast suspend/resume cycles.
153+
- Resuming and suspending can be partial-evaluated, leading to fast suspend/resume cycles.
153154

154-
Serialization is done entirely in guest-side code, by having the `Continuation` class implement `Externalizable`. The
155+
Serialization is done entirely in guest-side code, by having the `Continuation` class implement `Serializable`. The
155156
format is designed to enable backwards-compatible evolution of the format.

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/Symbol.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,10 +1018,10 @@ public static void ensureInitialized() {
10181018
"Lcom/oracle/truffle/espresso/polyglot/impl/EspressoForeignNumber;");
10191019

10201020
// Continuations
1021-
public static final Symbol<Type> org_graalvm_continuations_Continuation = StaticSymbols.putType(
1022-
"Lorg/graalvm/continuations/Continuation;");
1023-
public static final Symbol<Type> org_graalvm_continuations_Continuation_FrameRecord = StaticSymbols.putType(
1024-
"Lorg/graalvm/continuations/Continuation$FrameRecord;");
1021+
public static final Symbol<Type> org_graalvm_continuations_ContinuationImpl = StaticSymbols.putType(
1022+
"Lorg/graalvm/continuations/ContinuationImpl;");
1023+
public static final Symbol<Type> org_graalvm_continuations_ContinuationImpl_FrameRecord = StaticSymbols.putType(
1024+
"Lorg/graalvm/continuations/ContinuationImpl$FrameRecord;");
10251025
public static final Symbol<Type> org_graalvm_continuations_IllegalMaterializedRecordException = StaticSymbols.putType(
10261026
"Lorg/graalvm/continuations/IllegalMaterializedRecordException;");
10271027
public static final Symbol<Type> org_graalvm_continuations_IllegalContinuationStateException = StaticSymbols.putType(

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/LinkedKlassFieldLayout.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ private static class HiddenField {
240240
new HiddenField(Name.HIDDEN_TREGEX_SEARCH_FROM_BACKUP),
241241
new HiddenField(Name.HIDDEN_TREGEX_MATCHING_MODE_BACKUP)
242242
}),
243-
entry(Type.org_graalvm_continuations_Continuation, new HiddenField[]{
243+
entry(Type.org_graalvm_continuations_ContinuationImpl, new HiddenField[]{
244244
new HiddenField(Name.HIDDEN_CONTINUATION_FRAME_RECORD)
245245
}));
246246

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,36 +1928,36 @@ private DiffVersionLoadHelper diff() {
19281928
@CompilationFinal public Method java_beans_Introspector_flushFromCaches;
19291929

19301930
public final class ContinuumSupport {
1931-
public final Method org_graalvm_continuations_Continuation_run;
1932-
public final Method org_graalvm_continuations_Continuation_suspend;
1933-
public final Field org_graalvm_continuations_Continuation_stackFrameHead;
1931+
public final Method org_graalvm_continuations_ContinuationImpl_run;
1932+
public final Method org_graalvm_continuations_ContinuationImpl_suspend;
1933+
public final Field org_graalvm_continuations_ContinuationImpl_stackFrameHead;
19341934
public final Field HIDDEN_CONTINUATION_FRAME_RECORD;
1935-
public final ObjectKlass org_graalvm_continuations_Continuation_FrameRecord;
1936-
public final Field org_graalvm_continuations_Continuation_FrameRecord_pointers;
1937-
public final Field org_graalvm_continuations_Continuation_FrameRecord_primitives;
1938-
public final Field org_graalvm_continuations_Continuation_FrameRecord_method;
1939-
public final Field org_graalvm_continuations_Continuation_FrameRecord_next;
1940-
public final Field org_graalvm_continuations_Continuation_FrameRecord_bci;
1935+
public final ObjectKlass org_graalvm_continuations_ContinuationImpl_FrameRecord;
1936+
public final Field org_graalvm_continuations_ContinuationImpl_FrameRecord_pointers;
1937+
public final Field org_graalvm_continuations_ContinuationImpl_FrameRecord_primitives;
1938+
public final Field org_graalvm_continuations_ContinuationImpl_FrameRecord_method;
1939+
public final Field org_graalvm_continuations_ContinuationImpl_FrameRecord_next;
1940+
public final Field org_graalvm_continuations_ContinuationImpl_FrameRecord_bci;
19411941
public final ObjectKlass org_graalvm_continuations_IllegalMaterializedRecordException;
19421942
public final ObjectKlass org_graalvm_continuations_IllegalContinuationStateException;
19431943

19441944
private ContinuumSupport() {
1945-
ObjectKlass org_graalvm_continuations_Continuation = knownKlass(Type.org_graalvm_continuations_Continuation);
1946-
org_graalvm_continuations_Continuation_run = org_graalvm_continuations_Continuation.requireDeclaredMethod(Name.run, Signature._void);
1947-
org_graalvm_continuations_Continuation_suspend = org_graalvm_continuations_Continuation.requireDeclaredMethod(Name.suspend, Signature._void);
1948-
org_graalvm_continuations_Continuation_stackFrameHead = org_graalvm_continuations_Continuation.requireDeclaredField(Name.stackFrameHead,
1949-
Type.org_graalvm_continuations_Continuation_FrameRecord);
1950-
HIDDEN_CONTINUATION_FRAME_RECORD = org_graalvm_continuations_Continuation.requireHiddenField(Name.HIDDEN_CONTINUATION_FRAME_RECORD);
1951-
org_graalvm_continuations_Continuation_FrameRecord = knownKlass(Type.org_graalvm_continuations_Continuation_FrameRecord);
1952-
org_graalvm_continuations_Continuation_FrameRecord_pointers = org_graalvm_continuations_Continuation_FrameRecord.requireDeclaredField(
1945+
ObjectKlass org_graalvm_continuations_ContinuationImpl = knownKlass(Type.org_graalvm_continuations_ContinuationImpl);
1946+
org_graalvm_continuations_ContinuationImpl_run = org_graalvm_continuations_ContinuationImpl.requireDeclaredMethod(Name.run, Signature._void);
1947+
org_graalvm_continuations_ContinuationImpl_suspend = org_graalvm_continuations_ContinuationImpl.requireDeclaredMethod(Name.suspend, Signature._void);
1948+
org_graalvm_continuations_ContinuationImpl_stackFrameHead = org_graalvm_continuations_ContinuationImpl.requireDeclaredField(Name.stackFrameHead,
1949+
Type.org_graalvm_continuations_ContinuationImpl_FrameRecord);
1950+
HIDDEN_CONTINUATION_FRAME_RECORD = org_graalvm_continuations_ContinuationImpl.requireHiddenField(Name.HIDDEN_CONTINUATION_FRAME_RECORD);
1951+
org_graalvm_continuations_ContinuationImpl_FrameRecord = knownKlass(Type.org_graalvm_continuations_ContinuationImpl_FrameRecord);
1952+
org_graalvm_continuations_ContinuationImpl_FrameRecord_pointers = org_graalvm_continuations_ContinuationImpl_FrameRecord.requireDeclaredField(
19531953
Name.pointers, Type.java_lang_Object_array);
1954-
org_graalvm_continuations_Continuation_FrameRecord_primitives = org_graalvm_continuations_Continuation_FrameRecord.requireDeclaredField(
1954+
org_graalvm_continuations_ContinuationImpl_FrameRecord_primitives = org_graalvm_continuations_ContinuationImpl_FrameRecord.requireDeclaredField(
19551955
Name.primitives, Type._long_array);
1956-
org_graalvm_continuations_Continuation_FrameRecord_method = org_graalvm_continuations_Continuation_FrameRecord.requireDeclaredField(
1956+
org_graalvm_continuations_ContinuationImpl_FrameRecord_method = org_graalvm_continuations_ContinuationImpl_FrameRecord.requireDeclaredField(
19571957
Name.method, Type.java_lang_reflect_Method);
1958-
org_graalvm_continuations_Continuation_FrameRecord_next = org_graalvm_continuations_Continuation_FrameRecord.requireDeclaredField(
1959-
Name.next, Type.org_graalvm_continuations_Continuation_FrameRecord);
1960-
org_graalvm_continuations_Continuation_FrameRecord_bci = org_graalvm_continuations_Continuation_FrameRecord.requireDeclaredField(
1958+
org_graalvm_continuations_ContinuationImpl_FrameRecord_next = org_graalvm_continuations_ContinuationImpl_FrameRecord.requireDeclaredField(
1959+
Name.next, Type.org_graalvm_continuations_ContinuationImpl_FrameRecord);
1960+
org_graalvm_continuations_ContinuationImpl_FrameRecord_bci = org_graalvm_continuations_ContinuationImpl_FrameRecord.requireDeclaredField(
19611961
Name.bci, Type._int);
19621962
org_graalvm_continuations_IllegalMaterializedRecordException = knownKlass(
19631963
Type.org_graalvm_continuations_IllegalMaterializedRecordException);

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ public void createContinuableNode(int bci, int top) {
568568
return;
569569
} else {
570570
InstrumentationSupport instrument = instrumentation;
571-
int statementIndex = instrument == null ? 0 : instrument.hookBCIToNodeIndex.lookup(0, 0, bs.endBCI());
571+
int statementIndex = instrument == null ? 0 : instrument.getStatementIndexAfterJump(0, 0, bs.endBCI());
572572
quickenInvoke(top, bci, opcode, statementIndex);
573573
// continue loop, will execute at most once more.
574574
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/ContinuableMethodWithBytecode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public abstract static class ResumeNextContinuationNode extends EspressoNode {
8787
@Specialization(guards = "isLastRecord(records)")
8888
Object doLast(HostFrameRecord records) {
8989
assert records == null;
90-
assert ((EspressoRootNode) getRootNode()).getMethod() == getMeta().continuum.org_graalvm_continuations_Continuation_suspend;
90+
assert ((EspressoRootNode) getRootNode()).getMethod() == getMeta().continuum.org_graalvm_continuations_ContinuationImpl_suspend;
9191
// Was disabled in the call to Continuation.resume0().
9292
getLanguage().getThreadLocalState().enableSingleStepping();
9393
return StaticObject.NULL;

0 commit comments

Comments
 (0)