Skip to content

Commit c6b9e49

Browse files
committed
[GR-54300] Improve GC based disposal of polyglot engine and context.
1 parent e73ffa1 commit c6b9e49

File tree

69 files changed

+3148
-652
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+3148
-652
lines changed

sdk/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f
77
* GR-57681 Added the ability to use `Value#as(byte[].class)` to copy the contents of a guest language byte buffer (`Value#hasBufferElements()`) to a new byte array. The new functionality has precedence over accessing the guest object as array (`Value#hasArrayElements()`) if both ways are available.
88
* GR-57817 Starting with JDK 24 users now need to configure native access privileges to the java executable in order to avoid warnings from being printed by the JDK.
99
For usages of the module-path pass the `--enable-native-access=org.graalvm.truffle` option and for class-path usages pass the `--enable-native-access=ALL-UNNAMED` option to resolve the new warning. Note that Truffle automatically forwards the native access capability to all loaded languages and tools, therefore no further configuration is required. If the native access is denied by the user with `--illegal-native-access=deny` then loading the optimizing runtime will fail and the fallback runtime will be used. More information can be found in the integrity-by-default [JEP-472](https://openjdk.org/jeps/472).
10+
* GR-54300 `Context` and `Engine` is now automatically closed when no longer strongly referenced. A reachable `Value` or `PolyglotException` will keep the associated `Context` reachable. Additionally, the `Context` remains reachable when explicitly entered or if there is an active polyglot thread within it. The `Engine` remains reachable when there is a strongly reachable `Language`, `Instrument`, or `Context` instance. However, it is still recommended not to rely on garbage collection for closing. Instead, use the try-with-resources pattern for explicit context and engine management. For more information, refer to the [Automatic Close on GC documentation](https://github.com/oracle/graal/blob/master/truffle/docs/CloseOnGc.md).
1011

1112
## Version 24.1.0
1213
* GR-51177 Enable random offsets of runtime compiled function entry points for the UNTRUSTED polyglot sandbox policy.

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Context.java

+65-15
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
import java.io.InputStream;
4444
import java.io.OutputStream;
45+
import java.lang.ref.Reference;
4546
import java.nio.file.Path;
4647
import java.time.Duration;
4748
import java.time.Instant;
@@ -102,9 +103,10 @@
102103
* {@link #initialize(String) initialized}.
103104
* <li>Then, we assert the result value by converting the result value as primitive <code>int</code>
104105
* .
105-
* <li>Finally, if the context is no longer needed, it is necessary to close it to ensure that all
106-
* resources are freed. Contexts are also {@link AutoCloseable} for use with the Java
107-
* {@code try-with-resources} statement.
106+
* <li>Finally, when a context is no longer needed, it is recommended to explicitly close it to
107+
* ensure timely release of resources. While contexts are automatically closed when no longer
108+
* strongly reachable, explicit closure is still preferred. Contexts are also {@link AutoCloseable},
109+
* allowing them to be used with the Java {@code try-with-resources} statement.
108110
* </ul>
109111
*
110112
* <h3>Configuration</h3>
@@ -337,29 +339,40 @@ public final class Context implements AutoCloseable {
337339
final AbstractContextDispatch dispatch;
338340
final Object receiver;
339341
final Context currentAPI;
342+
final Context parent;
343+
/**
344+
* Strong reference to the creator {@link Context} to prevent it from being garbage collected
345+
* and closed while API {@link Context} is still reachable.
346+
*/
347+
final Context creatorContext;
340348
final Engine engine;
341349

342350
@SuppressWarnings("unchecked")
343-
<T> Context(AbstractContextDispatch dispatch, T receiver, Engine engine) {
351+
<T> Context(AbstractContextDispatch dispatch, T receiver, Context parentContext, Engine engine) {
344352
this.dispatch = dispatch;
345353
this.receiver = receiver;
346354
this.engine = engine;
347355
this.currentAPI = new Context(this);
348-
dispatch.setAPI(receiver, this);
356+
this.parent = parentContext;
357+
this.creatorContext = this;
349358
}
350359

351360
private Context() {
352361
this.dispatch = null;
353362
this.receiver = null;
354363
this.engine = null;
355364
this.currentAPI = null;
365+
this.parent = null;
366+
this.creatorContext = null;
356367
}
357368

358369
private <T> Context(Context creatorAPI) {
359370
this.dispatch = creatorAPI.dispatch;
360371
this.receiver = creatorAPI.receiver;
361372
this.engine = creatorAPI.engine.currentAPI;
362373
this.currentAPI = null;
374+
this.parent = creatorAPI.parent != null ? creatorAPI.parent.currentAPI : null;
375+
this.creatorContext = creatorAPI;
363376
}
364377

365378
/**
@@ -399,7 +412,11 @@ public Engine getEngine() {
399412
* @since 19.0
400413
*/
401414
public Value eval(Source source) {
402-
return (Value) dispatch.eval(receiver, source.getLanguage(), source);
415+
try {
416+
return (Value) dispatch.eval(receiver, source.getLanguage(), source);
417+
} finally {
418+
Reference.reachabilityFence(creatorContext);
419+
}
403420
}
404421

405422
/**
@@ -425,7 +442,11 @@ public Value eval(Source source) {
425442
* @since 19.0
426443
*/
427444
public Value eval(String languageId, CharSequence source) {
428-
return eval(Source.create(languageId, source));
445+
try {
446+
return eval(Source.create(languageId, source));
447+
} finally {
448+
Reference.reachabilityFence(creatorContext);
449+
}
429450
}
430451

431452
/**
@@ -480,7 +501,11 @@ public Value eval(String languageId, CharSequence source) {
480501
* @since 20.2
481502
*/
482503
public Value parse(Source source) throws PolyglotException {
483-
return (Value) dispatch.parse(receiver, source.getLanguage(), source);
504+
try {
505+
return (Value) dispatch.parse(receiver, source.getLanguage(), source);
506+
} finally {
507+
Reference.reachabilityFence(creatorContext);
508+
}
484509
}
485510

486511
/**
@@ -523,7 +548,11 @@ public Value parse(Source source) throws PolyglotException {
523548
* @since 20.2
524549
*/
525550
public Value parse(String languageId, CharSequence source) {
526-
return parse(Source.create(languageId, source));
551+
try {
552+
return parse(Source.create(languageId, source));
553+
} finally {
554+
Reference.reachabilityFence(creatorContext);
555+
}
527556
}
528557

529558
/**
@@ -543,7 +572,11 @@ public Value parse(String languageId, CharSequence source) {
543572
* @since 19.0
544573
*/
545574
public Value getPolyglotBindings() {
546-
return (Value) dispatch.getPolyglotBindings(receiver);
575+
try {
576+
return (Value) dispatch.getPolyglotBindings(receiver);
577+
} finally {
578+
Reference.reachabilityFence(creatorContext);
579+
}
547580
}
548581

549582
/**
@@ -560,7 +593,11 @@ public Value getPolyglotBindings() {
560593
* @since 19.0
561594
*/
562595
public Value getBindings(String languageId) {
563-
return (Value) dispatch.getBindings(receiver, languageId);
596+
try {
597+
return (Value) dispatch.getBindings(receiver, languageId);
598+
} finally {
599+
Reference.reachabilityFence(creatorContext);
600+
}
564601
}
565602

566603
/**
@@ -576,7 +613,11 @@ public Value getBindings(String languageId) {
576613
* @since 19.0
577614
*/
578615
public boolean initialize(String languageId) {
579-
return dispatch.initializeLanguage(receiver, languageId);
616+
try {
617+
return dispatch.initializeLanguage(receiver, languageId);
618+
} finally {
619+
Reference.reachabilityFence(creatorContext);
620+
}
580621
}
581622

582623
/**
@@ -586,6 +627,7 @@ public boolean initialize(String languageId) {
586627
*/
587628
public void resetLimits() {
588629
dispatch.resetLimits(receiver);
630+
Reference.reachabilityFence(creatorContext);
589631
}
590632

591633
/**
@@ -735,7 +777,11 @@ public void resetLimits() {
735777
* @since 19.0
736778
*/
737779
public Value asValue(Object hostValue) {
738-
return (Value) dispatch.asValue(receiver, hostValue);
780+
try {
781+
return (Value) dispatch.asValue(receiver, hostValue);
782+
} finally {
783+
Reference.reachabilityFence(creatorContext);
784+
}
739785
}
740786

741787
/**
@@ -754,6 +800,7 @@ public Value asValue(Object hostValue) {
754800
public void enter() {
755801
checkCreatorAccess("entered");
756802
dispatch.explicitEnter(receiver);
803+
Reference.reachabilityFence(creatorContext);
757804
}
758805

759806
/**
@@ -832,6 +879,7 @@ private void checkCreatorAccess(String operation) {
832879
public void close(boolean cancelIfExecuting) {
833880
checkCreatorAccess("closed");
834881
dispatch.close(receiver, cancelIfExecuting);
882+
Reference.reachabilityFence(creatorContext);
835883
}
836884

837885
/**
@@ -883,6 +931,7 @@ public void interrupt(Duration timeout) throws TimeoutException {
883931
if (!dispatch.interrupt(receiver, timeout)) {
884932
throw new TimeoutException("Interrupt timed out.");
885933
}
934+
Reference.reachabilityFence(creatorContext);
886935
}
887936

888937
/**
@@ -929,6 +978,7 @@ public void interrupt(Duration timeout) throws TimeoutException {
929978
*/
930979
public void safepoint() {
931980
dispatch.safepoint(receiver);
981+
Reference.reachabilityFence(creatorContext);
932982
}
933983

934984
/**
@@ -1934,11 +1984,11 @@ public Context build() {
19341984
}
19351985
Object logHandler = customLogHandler != null ? Engine.getImpl().newLogHandler(customLogHandler) : null;
19361986
String tmpDir = Engine.getImpl().getIO().hasHostFileAccess(useIOAccess) ? System.getProperty("java.io.tmpdir") : null;
1937-
ctx = (Context) engine.dispatch.createContext(engine.receiver, useSandboxPolicy, contextOut, contextErr, contextIn, hostClassLookupEnabled,
1987+
ctx = engine.dispatch.createContext(engine.receiver, engine, useSandboxPolicy, contextOut, contextErr, contextIn, hostClassLookupEnabled,
19381988
hostAccess, polyglotAccess, nativeAccess, createThread, hostClassLoading, innerContextOptions,
19391989
experimentalOptions, localHostLookupFilter, contextOptions, arguments == null ? Collections.emptyMap() : arguments,
19401990
permittedLanguages, useIOAccess, logHandler, createProcess, processHandler, useEnvironmentAccess, environment, zone, limits,
1941-
localCurrentWorkingDirectory, tmpDir, hostClassLoader, allowValueSharing, useSystemExit);
1991+
localCurrentWorkingDirectory, tmpDir, hostClassLoader, allowValueSharing, useSystemExit, true);
19421992
return ctx;
19431993
}
19441994

0 commit comments

Comments
 (0)