Skip to content

Commit 3f93353

Browse files
committed
Implement Engine.storeCache(...) method to persist engine caches manually.
1 parent c924a4e commit 3f93353

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
@@ -20,6 +20,7 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f
2020
* GR-66339 Truffle now checks that its multi-release classes ([JEP 238](https://openjdk.org/jeps/238)) are loaded correctly and provides a descriptive error message if they are not.
2121
* GR-55996 Added the options `engine.SourceCacheStatistics` and `engine.SourceCacheStatisticDetails` to print polyglot source cache statistics on engine close.
2222

23+
* 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.
2324

2425
## Version 24.2.0
2526
* 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
@@ -210,6 +210,8 @@ intf java.lang.AutoCloseable
210210
meth public !varargs static boolean copyResources(java.nio.file.Path,java.lang.String[]) throws java.io.IOException
211211
meth public !varargs static org.graalvm.polyglot.Engine create(java.lang.String[])
212212
meth public !varargs static org.graalvm.polyglot.Engine$Builder newBuilder(java.lang.String[])
213+
meth public boolean storeCache(java.nio.file.Path)
214+
meth public boolean storeCache(java.nio.file.Path,org.graalvm.nativeimage.c.type.WordPointer)
213215
meth public java.lang.String getImplementationName()
214216
meth public java.lang.String getVersion()
215217
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
@@ -1249,6 +1249,28 @@ <T extends TruffleLanguage<?>> PolyglotLanguage getLanguage(Class<T> languageCla
12491249
return foundLanguage;
12501250
}
12511251

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

1550+
record FinalizationResult(DispatchOutputStream out, DispatchOutputStream err, InputStream in) {
1551+
}
1552+
15281553
/**
15291554
* Invoked when the context is closing to prepare an engine to be stored.
15301555
*/
1531-
void finalizeStore() {
1556+
FinalizationResult finalizeStore() {
15321557
assert Thread.holdsLock(this.lock);
1533-
1558+
FinalizationResult result = new FinalizationResult(out, err, in);
15341559
this.out = null;
15351560
this.err = null;
15361561
this.in = null;
@@ -1544,6 +1569,13 @@ void finalizeStore() {
15441569
if (hostLanguageService != null) {
15451570
hostLanguageService.release();
15461571
}
1572+
return result;
1573+
}
1574+
1575+
void restoreStore(FinalizationResult result) {
1576+
this.out = result.out;
1577+
this.err = result.err;
1578+
this.in = result.in;
15471579
}
15481580

15491581
@TruffleBoundary

0 commit comments

Comments
 (0)