Skip to content

[GR-64947] Implement Engine.storeCache(...) method to persist engine caches manually. #11495

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f
* GR-64488 FileSystem implementations can now provide disk-related metadata, including [total space](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html##getFileStoreTotalSpace(java.nio.file.Path)), [usable space](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html#getFileStoreUsableSpace(java.nio.file.Path)), [unallocated space](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html#getFileStoreUnallocatedSpace(java.nio.file.Path)), [block size](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html#getFileStoreBlockSize(java.nio.file.Path)), and [read-only status](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html#isFileStoreReadOnly(java.nio.file.Path)).
* GR-22699(EE-only) Added the ability to spawn `Engine` or `Context` isolated in a separate process by setting `Context.Builder.option("engine.IsolateMode", "external").`.
* GR-64087 Removed the dependency on `org.graalvm.truffle:truffle-enterprise` from all language and tool POM artifacts. As a result, the community Maven artifacts (those with an artifact ID ending in `-community`) are now identical to their corresponding non-community artifacts. Consequently, all community language and tool POM artifacts have been deprecated. The only exception is `org.graalvm.truffle:java-community` vs. `org.graalvm.truffle:java`, which differ in the bundled Java runtime. Embedders using auxiliary engine caching, polyglot isolates, a isolated/untrusted sandbox policy, or the sandbox resource limits must now explicitly add the `org.graalvm.truffle:truffle-enterprise` Maven artifact to the classpath or module path.
* 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.

## Version 24.2.0
* 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.
Expand Down
2 changes: 2 additions & 0 deletions sdk/src/org.graalvm.polyglot/snapshot.sigtest
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ intf java.lang.AutoCloseable
meth public !varargs static boolean copyResources(java.nio.file.Path,java.lang.String[]) throws java.io.IOException
meth public !varargs static org.graalvm.polyglot.Engine create(java.lang.String[])
meth public !varargs static org.graalvm.polyglot.Engine$Builder newBuilder(java.lang.String[])
meth public boolean storeCache(java.nio.file.Path)
meth public boolean storeCache(java.nio.file.Path,org.graalvm.nativeimage.c.type.WordPointer)
meth public java.lang.String getImplementationName()
meth public java.lang.String getVersion()
meth public java.util.Map<java.lang.String,org.graalvm.polyglot.Instrument> getInstruments()
Expand Down
72 changes: 72 additions & 0 deletions sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
Expand All @@ -90,6 +91,7 @@
import org.graalvm.home.HomeFinder;
import org.graalvm.home.Version;
import org.graalvm.nativeimage.ImageInfo;
import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.options.OptionDescriptor;
import org.graalvm.options.OptionDescriptors;
import org.graalvm.polyglot.HostAccess.MutableTargetMapping;
Expand Down Expand Up @@ -126,6 +128,8 @@
import org.graalvm.polyglot.proxy.ProxyObject;
import org.graalvm.polyglot.proxy.ProxyTime;
import org.graalvm.polyglot.proxy.ProxyTimeZone;
import org.graalvm.word.Pointer;
import org.graalvm.word.WordFactory;

/**
* An execution engine for Graal {@linkplain Language guest languages} that allows to inspect the
Expand Down Expand Up @@ -316,6 +320,74 @@ public void close() {
close(false);
}

/**
* Stores the auxiliary engine cache to the targetFile without cancellation.
*
* @see #storeCache(Path, Pointer)
* @throws UnsupportedOperationException if this engine or the host virtual machine does not
* support storing the cache.
* @since 25.0
*/
public boolean storeCache(Path targetFile) throws UnsupportedOperationException {
return dispatch.storeCache(receiver, targetFile, WordFactory.nullPointer());
}

/**
* Stores the auxiliary engine cache to the {@code targetFile}. If it already exists, the file
* will be overwritten. The option <code>engine.CacheStoreEnabled</code> must be set to
* <code>true</code> to use this feature. Stored caches may be loaded by specifying the path
* using the <code>engine.CacheLoad</code> option.
* <p>
* Note that this feature is experimental and only supported on native-image hosts with
* Truffle's enterprise extensions.
* </p>
*
* <h3>Basic Usage:</h3>
*
* <pre>
* // Store the engine cache into a file
* Path store = Files.createTempFile("cache", "engine");
* try (Engine e = Engine.newBuilder().allowExperimentalOptions(true).option("engine.CacheStoreEnabled", "true").build()) {
* try (Context c = Context.newBuilder().engine(e).build()) {
* // Evaluate sources, run application
* }
* e.storeCache(store);
* }
*
* // Load the engine cache from a file
* try (Engine e = Engine.newBuilder().allowExperimentalOptions(true).option("engine.CacheLoad", store.toAbsolutePath().toString()).build()) {
* try (Context c = Context.newBuilder().engine(e).build()) {
* // The context should be able to use
* // the existing code cache.
* }
* }
* </pre>
*
* <p>
* See the <a href=
* "https://github.com/oracle/graal/blob/master/truffle/docs/AuxiliaryEngineCachingEnterprise.md">
* documentation</a> on auxiliary engine caching for further details.
* </p>
*
* @param targetFile the file to which the cache is stored
* @param cancelledWord a native pointer; if set to a non-zero value, the operation is
* cancelled. Allows cancellation of the cache store operation through a
* <code>cancelled</code> control word. The memory {@code address} pointing to the
* control word is polled periodically during storage without guaranteed frequency
* and may be delayed by safepoints such as garbage collection. A control word value
* of zero must be maintained for the duration of the operation. If a non-zero value
* is detected, the operation will be cancelled.
* @return <code>true</code> if the file was written; otherwise, <code>false</code>
* @throws CancellationException if the storeCache operation was cancelled via the
* <code>cancelled</code> pointer
* @throws UnsupportedOperationException if this engine or host virtual machine does not support
* cache storage
* @since 25.0
*/
public boolean storeCache(Path targetFile, WordPointer cancelledWord) throws CancellationException, UnsupportedOperationException {
return dispatch.storeCache(receiver, targetFile, cancelledWord);
}

/**
* Gets a human-readable name of the polyglot implementation (for example, "Default Truffle
* Engine" or "Graal Truffle Engine"). The returned value may change without notice. The value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
import java.util.function.Predicate;
import java.util.logging.LogRecord;

import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.options.OptionDescriptors;
import org.graalvm.options.OptionValues;
import org.graalvm.polyglot.Context;
Expand Down Expand Up @@ -850,6 +851,8 @@ public abstract Object attachExecutionListener(Object engine, Consumer<Object> o

public abstract void onEngineCollected(Object engineReceiver);

public abstract boolean storeCache(Object engineReceiver, Path targetFile, WordPointer cancelledWord);

}

public abstract static class AbstractExceptionDispatch extends AbstractDispatchClass {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
import com.oracle.svm.core.genscavenge.remset.RememberedSet;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.image.ImageHeap;
import com.oracle.svm.core.image.ImageHeapLayoutInfo;
import com.oracle.svm.core.image.ImageHeapLayouter;
import com.oracle.svm.core.image.ImageHeapLayoutInfo;
import com.oracle.svm.core.image.ImageHeapObject;
import com.oracle.svm.core.option.SubstrateOptionsParser;
import com.oracle.svm.core.util.UserError;
Expand Down Expand Up @@ -156,11 +156,12 @@ private Error reportHugeObjectError(ImageHeapObject info, String objectTypeMsg,
}

@Override
public ImageHeapLayoutInfo layout(ImageHeap imageHeap, int pageSize) {
public ImageHeapLayoutInfo layout(ImageHeap imageHeap, int pageSize, ImageHeapLayouterCallback callback) {
ImageHeapLayouterControl control = new ImageHeapLayouterControl(callback);
int objectAlignment = ConfigurationValues.getObjectLayout().getAlignment();
assert pageSize % objectAlignment == 0 : "Page size does not match object alignment";

ImageHeapLayoutInfo layoutInfo = doLayout(imageHeap, pageSize);
ImageHeapLayoutInfo layoutInfo = doLayout(imageHeap, pageSize, control);

for (ChunkedImageHeapPartition partition : getPartitions()) {
assert partition.getStartOffset() % partition.getStartAlignment() == 0 : partition;
Expand All @@ -170,15 +171,16 @@ public ImageHeapLayoutInfo layout(ImageHeap imageHeap, int pageSize) {
return layoutInfo;
}

private ImageHeapLayoutInfo doLayout(ImageHeap imageHeap, int pageSize) {
private ImageHeapLayoutInfo doLayout(ImageHeap imageHeap, int pageSize, ImageHeapLayouterControl control) {
allocator = new ChunkedImageHeapAllocator(imageHeap, startOffset);
for (ChunkedImageHeapPartition partition : getPartitions()) {
partition.layout(allocator);
control.poll();
partition.layout(allocator, control);
}
return populateInfoObjects(imageHeap.countAndVerifyDynamicHubs(), pageSize);
return populateInfoObjects(imageHeap.countAndVerifyDynamicHubs(), pageSize, control);
}

private ImageHeapLayoutInfo populateInfoObjects(int dynamicHubCount, int pageSize) {
private ImageHeapLayoutInfo populateInfoObjects(int dynamicHubCount, int pageSize, ImageHeapLayouterControl control) {
// Determine writable start boundary from chunks: a chunk that contains writable objects
// must also have a writable card table
long offsetOfFirstWritableAlignedChunk = -1;
Expand All @@ -188,6 +190,8 @@ private ImageHeapLayoutInfo populateInfoObjects(int dynamicHubCount, int pageSiz
break; // (chunks are in ascending memory order)
}
}
control.poll();

VMError.guarantee(offsetOfFirstWritableAlignedChunk >= 0 && offsetOfFirstWritableAlignedChunk % pageSize == 0, "Start of the writable part is assumed to be page-aligned");
long offsetOfFirstWritableUnalignedChunk = -1;
long offsetOfLastWritableUnalignedChunk = -1;
Expand All @@ -200,13 +204,16 @@ private ImageHeapLayoutInfo populateInfoObjects(int dynamicHubCount, int pageSiz
}
offsetOfLastWritableUnalignedChunk = chunk.getBegin();
}
control.poll();

heapInfo.initialize(getReadOnlyRegular().firstObject, getReadOnlyRegular().lastObject, getReadOnlyRelocatable().firstObject, getReadOnlyRelocatable().lastObject,
getWritablePatched().firstObject, getWritablePatched().lastObject,
getWritableRegular().firstObject, getWritableRegular().lastObject, getWritableHuge().firstObject, getWritableHuge().lastObject,
getReadOnlyHuge().firstObject, getReadOnlyHuge().lastObject, offsetOfFirstWritableAlignedChunk, offsetOfFirstWritableUnalignedChunk, offsetOfLastWritableUnalignedChunk,
dynamicHubCount);

control.poll();

long writableEnd = getWritableHuge().getStartOffset() + getWritableHuge().getSize();
long writableSize = writableEnd - offsetOfFirstWritableAlignedChunk;
long imageHeapSize = getReadOnlyHuge().getStartOffset() + getReadOnlyHuge().getSize() - startOffset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.TreeMap;

import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.core.image.ImageHeapLayouter.ImageHeapLayouterControl;
import com.oracle.svm.core.image.ImageHeapObject;
import com.oracle.svm.core.image.ImageHeapPartition;

Expand Down Expand Up @@ -72,44 +73,46 @@ void assign(ImageHeapObject obj) {
objects.add(obj);
}

void layout(ChunkedImageHeapAllocator allocator) {
void layout(ChunkedImageHeapAllocator allocator, ImageHeapLayouterControl control) {
if (hugeObjects) {
layoutInUnalignedChunks(allocator);
layoutInUnalignedChunks(allocator, control);
} else {
layoutInAlignedChunks(allocator);
layoutInAlignedChunks(allocator, control);
}
}

private void layoutInUnalignedChunks(ChunkedImageHeapAllocator allocator) {
private void layoutInUnalignedChunks(ChunkedImageHeapAllocator allocator, ImageHeapLayouterControl control) {
allocator.finishAlignedChunk();
allocator.alignBetweenChunks(getStartAlignment());
startOffset = allocator.getPosition();

for (ImageHeapObject info : objects) { // No need to sort by size
appendAllocatedObject(info, allocator.allocateUnalignedChunkForObject(info, isWritable()));
control.poll();
}

allocator.alignBetweenChunks(getEndAlignment());
endOffset = allocator.getPosition();
}

private void layoutInAlignedChunks(ChunkedImageHeapAllocator allocator) {
private void layoutInAlignedChunks(ChunkedImageHeapAllocator allocator, ImageHeapLayouterControl control) {
allocator.maybeStartAlignedChunk();
allocator.alignInAlignedChunk(getStartAlignment());
startOffset = allocator.getPosition();

allocateObjectsInAlignedChunks(allocator);
allocateObjectsInAlignedChunks(allocator, control);

allocator.alignInAlignedChunk(getEndAlignment());
endOffset = allocator.getPosition();
}

private void allocateObjectsInAlignedChunks(ChunkedImageHeapAllocator allocator) {
private void allocateObjectsInAlignedChunks(ChunkedImageHeapAllocator allocator, ImageHeapLayouterControl control) {
NavigableMap<Long, Queue<ImageHeapObject>> sortedObjects = createSortedObjectsMap();
while (!sortedObjects.isEmpty()) {
ImageHeapObject info = dequeueBestFit(sortedObjects, allocator.getRemainingBytesInAlignedChunk());
if (info == null) {
allocator.startNewAlignedChunk();
control.poll();
} else {
appendAllocatedObject(info, allocator.allocateObjectInAlignedChunk(info, isWritable()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ public interface ImageHeapLayouter {
void assignObjectToPartition(ImageHeapObject info, boolean immutable, boolean references, boolean relocatable, boolean patched);

/**
* Places all heap partitions and assigns objects their final offsets.
* Places all heap partitions and assigns objects their final offsets. The layout operation can
* be cancelled through the {@link ImageHeapLayouterCallback}. If the layout is cancelled, an
* instance of {@link ImageHeapLayoutCancelledException} is thrown.
*/
ImageHeapLayoutInfo layout(ImageHeap imageHeap, int pageSize);
ImageHeapLayoutInfo layout(ImageHeap imageHeap, int pageSize, ImageHeapLayouterCallback callback);

/** Hook to run tasks after heap layout is finished. */
@SuppressWarnings("unused")
Expand All @@ -63,4 +65,39 @@ default void afterLayout(ImageHeap imageHeap) {
* same offset in the given buffer, the same offset must be specified to this method.
*/
void writeMetadata(ByteBuffer imageHeapBytes, long imageHeapOffsetInBuffer);

/**
* Facilitates {@link ImageHeapLayouter#layout} cancellation through an
* {@link ImageHeapLayouterCallback} instance.
*/
class ImageHeapLayouterControl {
protected final ImageHeapLayouterCallback callback;

public ImageHeapLayouterControl(ImageHeapLayouterCallback callback) {
this.callback = callback;
}

public void poll() throws ImageHeapLayoutCancelledException {
if (callback.shouldCancel()) {
throw new ImageHeapLayoutCancelledException();
}
}
}

interface ImageHeapLayouterCallback {

ImageHeapLayouterCallback NONE = () -> false;

/**
* Called periodically to determine whether the operation should be canceled.
*/
boolean shouldCancel();
}

class ImageHeapLayoutCancelledException extends RuntimeException {
private static final long serialVersionUID = 1017980175582546348L;

public ImageHeapLayoutCancelledException() {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
import com.oracle.svm.core.graal.nodes.TLABObjectHeaderConstant;
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.image.ImageHeapLayoutInfo;
import com.oracle.svm.core.image.ImageHeapLayouter.ImageHeapLayouterCallback;
import com.oracle.svm.core.image.ImageHeapPartition;
import com.oracle.svm.core.imagelayer.DynamicImageLayerInfo;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
Expand Down Expand Up @@ -436,7 +437,7 @@ public void build(String imageName, DebugContext debug) {

long roSectionSize = codeCache.getAlignedConstantsSize();
long rwSectionSize = ConfigurationValues.getObjectLayout().alignUp(cGlobals.getSize());
heapLayout = heap.getLayouter().layout(heap, objectFile.getPageSize());
heapLayout = heap.getLayouter().layout(heap, objectFile.getPageSize(), ImageHeapLayouterCallback.NONE);
// after this point, the layout is final and must not be changed anymore
assert !hasDuplicatedObjects(heap.getObjects()) : "heap.getObjects() must not contain any duplicates";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.nio.file.Path;
import java.time.ZoneId;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;

import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.options.OptionDescriptors;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
Expand Down Expand Up @@ -188,4 +190,10 @@ public void onEngineCollected(Object receiver) {
dispatch.onEngineCollected(engineReceiver);
hostToGuest.shutdown(engine.remoteEngine);
}

@Override
public boolean storeCache(Object engineReceiver, Path targetFile, WordPointer cancelWord) {
throw new UnsupportedOperationException();
}

}
Loading