Skip to content

Commit c8e6507

Browse files
committed
[GR-64947] Implement Engine.storeCache(...) method to persist engine caches manually.
PullRequest: graal/21259
2 parents c26d43d + 3f93353 commit c8e6507

File tree

16 files changed

+197
-8
lines changed

16 files changed

+197
-8
lines changed

sdk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f
2222
* GR-55996 Added the options `engine.SourceCacheStatistics` and `engine.SourceCacheStatisticDetails` to print polyglot source cache statistics on engine close.
2323

2424
* GR-65561 Added `Context.Builder#apply`, `ContextBuilder#extendIO`, and `ContextBuilder#extendHostAccess` to enable composable Context configuration
25+
* GR-64947 Added `Engine.storeCache(Path)` to manually store [auxiliary engine caches](https://github.com/oracle/graal/blob/master/truffle/docs/AuxiliaryEngineCachingEnterprise.md) when needed.
2526

2627
## Version 24.2.0
2728
* GR-54905 When using Truffle NFI with the Panama backend, native access must now be granted to the Truffle module instead of the NFI Panama module. Use the `--enable-native-access=org.graalvm.truffle` Java command line option to enable the native access for the NFI Panama backend.

sdk/src/org.graalvm.polyglot/snapshot.sigtest

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ intf java.lang.AutoCloseable
213213
meth public !varargs static boolean copyResources(java.nio.file.Path,java.lang.String[]) throws java.io.IOException
214214
meth public !varargs static org.graalvm.polyglot.Engine create(java.lang.String[])
215215
meth public !varargs static org.graalvm.polyglot.Engine$Builder newBuilder(java.lang.String[])
216+
meth public boolean storeCache(java.nio.file.Path)
217+
meth public boolean storeCache(java.nio.file.Path,org.graalvm.nativeimage.c.type.WordPointer)
216218
meth public java.lang.String getImplementationName()
217219
meth public java.lang.String getVersion()
218220
meth public java.util.Map<java.lang.String,org.graalvm.polyglot.Instrument> getInstruments()

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

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
import java.util.Properties;
8181
import java.util.ServiceLoader;
8282
import java.util.Set;
83+
import java.util.concurrent.CancellationException;
8384
import java.util.concurrent.atomic.AtomicReference;
8485
import java.util.function.Consumer;
8586
import java.util.function.Function;
@@ -90,6 +91,7 @@
9091
import org.graalvm.home.HomeFinder;
9192
import org.graalvm.home.Version;
9293
import org.graalvm.nativeimage.ImageInfo;
94+
import org.graalvm.nativeimage.c.type.WordPointer;
9395
import org.graalvm.options.OptionDescriptor;
9496
import org.graalvm.options.OptionDescriptors;
9597
import org.graalvm.polyglot.HostAccess.MutableTargetMapping;
@@ -316,6 +318,76 @@ public void close() {
316318
close(false);
317319
}
318320

321+
/**
322+
* Stores the auxiliary engine cache to the targetFile without cancellation.
323+
*
324+
* @see #storeCache(Path, WordPointer)
325+
* @throws UnsupportedOperationException if this engine or the host virtual machine does not
326+
* support storing the cache.
327+
* @since 25.0
328+
*/
329+
public boolean storeCache(Path targetFile) throws UnsupportedOperationException {
330+
return dispatch.storeCache(receiver, targetFile, 0L);
331+
}
332+
333+
/**
334+
* Stores the auxiliary engine cache to the {@code targetFile}. If it already exists, the file
335+
* will be overwritten. The option <code>engine.CacheStoreEnabled</code> must be set to
336+
* <code>true</code> to use this feature. Stored caches may be loaded by specifying the path
337+
* using the <code>engine.CacheLoad</code> option.
338+
* <p>
339+
* Note that this feature is experimental and only supported on native-image hosts with
340+
* Truffle's enterprise extensions.
341+
* </p>
342+
*
343+
* <h3>Basic Usage:</h3>
344+
*
345+
* <pre>
346+
* // Store the engine cache into a file
347+
* Path store = Files.createTempFile("cache", "engine");
348+
* try (Engine e = Engine.newBuilder().allowExperimentalOptions(true).option("engine.CacheStoreEnabled", "true").build()) {
349+
* try (Context c = Context.newBuilder().engine(e).build()) {
350+
* // Evaluate sources, run application
351+
* }
352+
* e.storeCache(store);
353+
* }
354+
*
355+
* // Load the engine cache from a file
356+
* try (Engine e = Engine.newBuilder().allowExperimentalOptions(true).option("engine.CacheLoad", store.toAbsolutePath().toString()).build()) {
357+
* try (Context c = Context.newBuilder().engine(e).build()) {
358+
* // The context should be able to use
359+
* // the existing code cache.
360+
* }
361+
* }
362+
* </pre>
363+
*
364+
* <p>
365+
* See the <a href=
366+
* "https://github.com/oracle/graal/blob/master/truffle/docs/AuxiliaryEngineCachingEnterprise.md">
367+
* documentation</a> on auxiliary engine caching for further details.
368+
* </p>
369+
*
370+
* @param targetFile the file to which the cache is stored
371+
* @param cancelledWord a native pointer; if set to a non-zero value, the operation is
372+
* cancelled. Allows cancellation of the cache store operation through a
373+
* <code>cancelled</code> control word. The memory {@code address} pointing to the
374+
* control word is polled periodically during storage without guaranteed frequency
375+
* and may be delayed by safepoints such as garbage collection. A control word value
376+
* of zero must be maintained for the duration of the operation. If a non-zero value
377+
* is detected, the operation will be cancelled. A non-null provided pointer must
378+
* remain accessible during the entire operation. Providing an invalid or
379+
* inaccessible pointer may result in a VM crash.
380+
* @return <code>true</code> if the file was written; otherwise, <code>false</code>
381+
* @throws CancellationException if the storeCache operation was cancelled via the
382+
* <code>cancelled</code> pointer
383+
* @throws UnsupportedOperationException if this engine or host virtual machine does not support
384+
* cache storage
385+
* @since 25.0
386+
*/
387+
public boolean storeCache(Path targetFile, WordPointer cancelledWord) throws CancellationException, UnsupportedOperationException {
388+
return dispatch.storeCache(receiver, targetFile, cancelledWord.rawValue());
389+
}
390+
319391
/**
320392
* Gets a human-readable name of the polyglot implementation (for example, "Default Truffle
321393
* Engine" or "Graal Truffle Engine"). The returned value may change without notice. The value

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,8 @@ public abstract Object attachExecutionListener(Object engine, Consumer<Object> o
852852

853853
public abstract void onEngineCollected(Object engineReceiver);
854854

855+
public abstract boolean storeCache(Object engineReceiver, Path targetFile, long cancelledWord);
856+
855857
}
856858

857859
public abstract static class AbstractExceptionDispatch extends AbstractDispatchClass {

truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostEngineDispatch.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import java.io.InputStream;
4444
import java.io.OutputStream;
4545
import java.lang.ref.Reference;
46+
import java.nio.file.Path;
4647
import java.time.ZoneId;
4748
import java.util.Map;
4849
import java.util.Set;
@@ -188,4 +189,10 @@ public void onEngineCollected(Object receiver) {
188189
dispatch.onEngineCollected(engineReceiver);
189190
hostToGuest.shutdown(engine.remoteEngine);
190191
}
192+
193+
@Override
194+
public boolean storeCache(Object engineReceiver, Path targetFile, long cancelledWord) {
195+
throw new UnsupportedOperationException();
196+
}
197+
191198
}

truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,9 @@ public abstract <T, G> Iterator<T> mergeHostGuestFrames(Object polyglotEngine, S
694694

695695
public abstract void preinitializeContext(Object polyglotEngine);
696696

697-
public abstract void finalizeStore(Object polyglotEngine);
697+
public abstract Object finalizeStore(Object polyglotEngine);
698+
699+
public abstract void restoreStore(Object polyglotEngine, Object finalizationResult);
698700

699701
public abstract Object getEngineLock(Object polyglotEngine);
700702

@@ -1297,6 +1299,8 @@ public final FrameExtensions getFrameExtensionsUnsafe() {
12971299

12981300
public abstract boolean onEngineClosing(Object runtimeData);
12991301

1302+
public abstract boolean onStoreCache(Object runtimeData, Path targetPath, long cancelledWord);
1303+
13001304
public abstract void onEngineClosed(Object runtimeData);
13011305

13021306
public abstract boolean isOSRRootNode(RootNode rootNode);

truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultRuntimeAccessor.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
*/
4141
package com.oracle.truffle.api.impl;
4242

43+
import java.nio.file.Path;
4344
import java.util.function.Consumer;
4445
import java.util.function.Function;
4546
import java.util.function.Supplier;
@@ -255,6 +256,11 @@ public boolean onEngineClosing(Object runtimeData) {
255256
return false;
256257
}
257258

259+
@Override
260+
public boolean onStoreCache(Object runtimeData, Path targetPath, long cancelledWord) {
261+
throw new UnsupportedOperationException("Persisting an engine is not supported with the the Truffle fallback runtime. It is only supported on native-image hosts.");
262+
}
263+
258264
@Override
259265
public boolean isOSRRootNode(RootNode rootNode) {
260266
return false;

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
import com.oracle.truffle.api.source.SourceSection;
131131
import com.oracle.truffle.polyglot.FileSystems.ResetablePath;
132132
import com.oracle.truffle.polyglot.PolyglotContextConfig.FileSystemConfig;
133+
import com.oracle.truffle.polyglot.PolyglotEngineImpl.FinalizationResult;
133134
import com.oracle.truffle.polyglot.PolyglotImpl.EmbedderFileSystemContext;
134135
import com.oracle.truffle.polyglot.PolyglotImpl.VMObject;
135136
import com.oracle.truffle.polyglot.PolyglotLocals.InstrumentContextLocal;
@@ -1323,8 +1324,13 @@ public void preinitializeContext(Object polyglotEngine) {
13231324
}
13241325

13251326
@Override
1326-
public void finalizeStore(Object polyglotEngine) {
1327-
((PolyglotEngineImpl) polyglotEngine).finalizeStore();
1327+
public Object finalizeStore(Object polyglotEngine) {
1328+
return ((PolyglotEngineImpl) polyglotEngine).finalizeStore();
1329+
}
1330+
1331+
@Override
1332+
public void restoreStore(Object polyglotEngine, Object finalizationResult) {
1333+
((PolyglotEngineImpl) polyglotEngine).restoreStore((FinalizationResult) finalizationResult);
13281334
}
13291335

13301336
@Override

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineDispatch.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import java.io.InputStream;
4444
import java.io.OutputStream;
4545
import java.lang.ref.Reference;
46+
import java.nio.file.Path;
4647
import java.time.ZoneId;
4748
import java.util.ArrayList;
4849
import java.util.HashMap;
@@ -327,4 +328,11 @@ public SandboxPolicy getSandboxPolicy(Object engineReceiver) {
327328
public void onEngineCollected(Object engineReceiver) {
328329
((PolyglotEngineImpl) engineReceiver).onEngineCollected();
329330
}
331+
332+
@Override
333+
public boolean storeCache(Object engineReceiver, Path targetFile, long cancelledWord) {
334+
PolyglotEngineImpl engine = ((PolyglotEngineImpl) engineReceiver);
335+
return engine.storeCache(targetFile, cancelledWord);
336+
}
337+
330338
}

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineImpl.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,28 @@ <T extends TruffleLanguage<?>> PolyglotLanguage getLanguage(Class<T> languageCla
12531253
return foundLanguage;
12541254
}
12551255

1256+
boolean storeCache(Path targetPath, long cancelledWord) {
1257+
if (!TruffleOptions.AOT) {
1258+
throw new UnsupportedOperationException("Storing the engine cache is only supported on native-image hosts.");
1259+
}
1260+
1261+
synchronized (this.lock) {
1262+
if (closingThread != null || closed) {
1263+
throw new IllegalStateException("The engine is already closed and cannot be cancelled or persisted.");
1264+
}
1265+
if (!storeEngine) {
1266+
throw new IllegalStateException(
1267+
"In order to store the cache the option 'engine.CacheStoreEnabled' must be set to 'true'.");
1268+
}
1269+
List<PolyglotContextImpl> localContexts = collectAliveContexts();
1270+
if (!localContexts.isEmpty()) {
1271+
throw new IllegalStateException("There are still alive contexts that need to be closed or cancelled before the engine can be persisted.");
1272+
}
1273+
1274+
return RUNTIME.onStoreCache(this.runtimeData, targetPath, cancelledWord);
1275+
}
1276+
}
1277+
12561278
void ensureClosed(boolean force, boolean initiatedByContext) {
12571279
synchronized (this.lock) {
12581280
Thread currentThread = Thread.currentThread();
@@ -1529,12 +1551,15 @@ void preInitialize() {
15291551
}
15301552
}
15311553

1554+
record FinalizationResult(DispatchOutputStream out, DispatchOutputStream err, InputStream in) {
1555+
}
1556+
15321557
/**
15331558
* Invoked when the context is closing to prepare an engine to be stored.
15341559
*/
1535-
void finalizeStore() {
1560+
FinalizationResult finalizeStore() {
15361561
assert Thread.holdsLock(this.lock);
1537-
1562+
FinalizationResult result = new FinalizationResult(out, err, in);
15381563
this.out = null;
15391564
this.err = null;
15401565
this.in = null;
@@ -1548,6 +1573,13 @@ void finalizeStore() {
15481573
if (hostLanguageService != null) {
15491574
hostLanguageService.release();
15501575
}
1576+
return result;
1577+
}
1578+
1579+
void restoreStore(FinalizationResult result) {
1580+
this.out = result.out;
1581+
this.err = result.err;
1582+
this.in = result.in;
15511583
}
15521584

15531585
@TruffleBoundary

0 commit comments

Comments
 (0)