Skip to content
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
54 changes: 54 additions & 0 deletions src/hotspot/share/include/crlib/crlib_checkpoint_availability.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2026, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef CRLIB_CHECKPOINT_AVAILABILITY_H
#define CRLIB_CHECKPOINT_AVAILABILITY_H
Comment on lines +23 to +24

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me "checkpoint is available" means that there is an existing checkpoint image from which we can restore, while the extension is for checking it is currently possible/permitted to create a checkpoint image.


#include "crlib.h"

#ifdef __cplusplus
extern "C" {
#endif

#define CRLIB_EXTENSION_CHECKPOINT_AVAILABILITY_NAME "checkpointable data"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#define CRLIB_EXTENSION_CHECKPOINT_AVAILABILITY_NAME "checkpointable data"
#define CRLIB_EXTENSION_CHECKPOINT_AVAILABILITY_NAME "checkpoint availability"

#define CRLIB_EXTENSION_CHECKPOINTABLE(api) \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be CRLIB_EXTENSION_CHECKPOINT_AVAILABILITY to follow naming traditions of other extensions

CRLIB_EXTENSION(api, crlib_checkpoint_availability_t, CRLIB_EXTENSION_CHECKPOINT_CRLIB_CHECKPOINT_AVAILABILITY_NAME)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A smoke test for the feature would reveal typos like this

Suggested change
CRLIB_EXTENSION(api, crlib_checkpoint_availability_t, CRLIB_EXTENSION_CHECKPOINT_CRLIB_CHECKPOINT_AVAILABILITY_NAME)
CRLIB_EXTENSION(api, crlib_checkpoint_availability_t, CRLIB_EXTENSION_CHECKPOINT_AVAILABILITY_NAME)


typedef int crlib_checkpointable_status_t;
#define CRLIB_CHECKPOINTABLE_NEVER 0 // engine will never accept another checkpoint
#define CRLIB_CHECKPOINTABLE_NOT_YET 1 // not now; may become ready later
#define CRLIB_CHECKPOINTABLE_READY 2 // checkpoint can proceed
Comment on lines +37 to +39

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpicks, checkpoint can be accepted to restore from but that is not what was meant here

Suggested change
#define CRLIB_CHECKPOINTABLE_NEVER 0 // engine will never accept another checkpoint
#define CRLIB_CHECKPOINTABLE_NOT_YET 1 // not now; may become ready later
#define CRLIB_CHECKPOINTABLE_READY 2 // checkpoint can proceed
#define CRLIB_CHECKPOINTABLE_NEVER 0 // engine will never create another checkpoint
#define CRLIB_CHECKPOINTABLE_NOT_YET 1 // not now; may become ready later
#define CRLIB_CHECKPOINTABLE_READY 2 // checkpoint can be created



Comment on lines +40 to +41

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

// API for obtaining information about chackpointable status.
struct crlib_checkpoint_availability {
crlib_extension_t header;

crlib_checkpointable_status_t (*get_checkpointable_status)(crlib_conf_t *);
};
typedef const struct crlib_checkpoint_availability crlib_checkpoint_availability_t;

#ifdef __cplusplus
} // extern "C
#endif

#endif // CRLIB_CHECKPOINT_AVAILABILITY_H

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#endif // CRLIB_CHECKPOINT_AVAILABILITY_H
#endif // CRLIB_CHECKPOINT_AVAILABILITY_H

3 changes: 3 additions & 0 deletions src/hotspot/share/include/jvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,9 @@ enum cr_fail_type {
JNIEXPORT jobjectArray JNICALL
JVM_Checkpoint(JNIEnv *env, jarray fd_arr, jobjectArray obj_arr, jboolean dry_run, jlong jcmd_stream);

JNIEXPORT jint JNICALL
JVM_GetCheckpointableStatus(JNIEnv *env);

JNIEXPORT void JNICALL
JVM_StartRecordingDecompilations(JNIEnv *env);

Expand Down
4 changes: 4 additions & 0 deletions src/hotspot/share/prims/jvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3664,6 +3664,10 @@ JVM_ENTRY(jobjectArray, JVM_Checkpoint(JNIEnv *env, jarray fd_arr, jobjectArray
return (jobjectArray) JNIHandles::make_local(THREAD, ret());
JVM_END

JVM_ENTRY(jint, JVM_GetCheckpointableStatus(JNIEnv *env))
return crac::checkpointable_status();
JVM_END

JVM_ENTRY(void, JVM_StartRecordingDecompilations(JNIEnv *env))
CRaCRecompiler::start_recording_decompilations();
JVM_END
Expand Down
20 changes: 19 additions & 1 deletion src/hotspot/share/runtime/crac.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Azul Systems, Inc. All rights reserved.
* Copyright (c) 2023, 2026, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -80,6 +80,24 @@ jlong crac::uptime_since_restore() {
return os::javaTimeNanos() - _restore_start_nanos;
}

jint crac::checkpointable_status() {
if (crac::_engine->prepare_checkpoint_availability_api() == CracEngine::ApiStatus::OK) {
crlib_checkpointable_status_t status = crac::_engine->get_checkpointable_status();
if (status == CRLIB_CHECKPOINTABLE_NEVER) {
if (crac::_generation > 1) {
Comment on lines +84 to +87

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crac:: prefix should not be needed in this method

Suggested change
if (crac::_engine->prepare_checkpoint_availability_api() == CracEngine::ApiStatus::OK) {
crlib_checkpointable_status_t status = crac::_engine->get_checkpointable_status();
if (status == CRLIB_CHECKPOINTABLE_NEVER) {
if (crac::_generation > 1) {
if (_engine->prepare_checkpoint_availability_api() == CracEngine::ApiStatus::OK) {
crlib_checkpointable_status_t status = _engine->get_checkpointable_status();
if (status == CRLIB_CHECKPOINTABLE_NEVER) {
if (_generation > 1) {

return status;
} else {
// Engine doesn't support checkpoint after restore,
// but it's a first checkpoint. So return READY.
return CRLIB_CHECKPOINTABLE_READY;
Comment on lines +90 to +92

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not override what the engine reports, it is very unintuitive

}
}
return status;
}

return CRLIB_CHECKPOINTABLE_READY;
}

void VM_Crac::print_resources(const char* msg, ...) {
if (CRaCPrintResourcesOnCheckpoint) {
va_list ap;
Expand Down
3 changes: 2 additions & 1 deletion src/hotspot/share/runtime/crac.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Azul Systems, Inc. All rights reserved.
* Copyright (c) 2023, 2026, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -57,6 +57,7 @@ class crac: AllStatic {

static jlong restore_start_time();
static jlong uptime_since_restore();
static jint checkpointable_status();
Comment thread
antonvoznia marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: since we have crlib_checkpointable_status_t we should return that and convert to jint in the JNI layer


static jlong monotonic_time_offset() {
return _javaTimeNanos_offset;
Expand Down
12 changes: 11 additions & 1 deletion src/hotspot/share/runtime/crac_engine.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Azul Systems, Inc. All rights reserved.
* Copyright (c) 2025, 2026, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -606,3 +606,13 @@ CracEngine::ApiStatus CracEngine::prepare_image_score_api() {
bool CracEngine::set_score(const char* metric, double value) {
return _image_score_api->set_score(_conf, metric, value);
}

CracEngine::ApiStatus CracEngine::prepare_checkpoint_availability_api() {
prepare_extension_api(_checkpoint_availability_api, CRLIB_EXTENSION_CHECKPOINT_AVAILABILITY_NAME)
require_method(get_checkpointable_status)
complete_extension_api(_checkpoint_availability_api)
}

crlib_checkpointable_status_t CracEngine::get_checkpointable_status() {
return _checkpoint_availability_api->get_checkpointable_status(_conf);
}
7 changes: 6 additions & 1 deletion src/hotspot/share/runtime/crac_engine.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Azul Systems, Inc. All rights reserved.
* Copyright (c) 2025, 2026, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -25,6 +25,7 @@
#define SHARE_RUNTIME_CRAC_ENGINE_HPP

#include "crlib/crlib.h"
#include "crlib/crlib_checkpoint_availability.h"
#include "crlib/crlib_description.h"
#include "crlib/crlib_image_constraints.h"
#include "crlib/crlib_image_score.h"
Expand Down Expand Up @@ -79,6 +80,9 @@ class CracEngine : public CHeapObj<mtInternal> {
ApiStatus prepare_image_score_api();
bool set_score(const char* metric, double value);

ApiStatus prepare_checkpoint_availability_api();
crlib_checkpointable_status_t get_checkpointable_status();

private:
char _name[MAX_ENGINE_LENGTH];
void *_lib = nullptr;
Expand All @@ -90,6 +94,7 @@ class CracEngine : public CHeapObj<mtInternal> {
crlib_description_t *_description_api = nullptr;
crlib_image_constraints_t *_image_constraints_api = nullptr;
crlib_image_score_t *_image_score_api = nullptr;
crlib_checkpoint_availability_t *_checkpoint_availability_api = nullptr;

crlib_conf_option_t *_options = nullptr;
};
Expand Down
22 changes: 22 additions & 0 deletions src/java.base/linux/native/libcriuengine/criuengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <sys/wait.h>
#include <unistd.h>

#include "crlib/crlib_checkpoint_availability.h"
#include "crlib/crlib_description.h"
#include "crlib/crlib_restore_data.h"
#include "crlib/crlib_user_data.h"
Expand Down Expand Up @@ -495,6 +496,18 @@ static char* get_relative_file(const char* rel) {
return buf;
}

static crlib_checkpointable_status_t get_checkpointable_status(crlib_conf_t * conf) {
if (!conf->direct_map()) {
return CRLIB_CHECKPOINTABLE_READY;
}
Comment on lines +500 to +502

@TimPushkin TimPushkin May 26, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think of it, I do not think this would actually work. On restore there are two instances of the engine: (1) the restoring one and (2) the one being restored. Options are not propagated from (1) to (2) this way. direct_map is set on restore i.e. only in (1), while this check is run before checkpoint i.e. in (2). So I believe this would work only if you set direct_map on the initial start up (which is incorrect and should give you a warning on checkpoint).

I think you would need to inspect the memory of the process to see if it comes from a mmaped file to make this work.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't do any introspection, that's not in line with what CRIU does. The restoring engine should rather transfer data to checkpointed engine (we do that for env and system properties, though the implementation passes only the PID number and the 'data' is actually transferred at JVM level)

@TimPushkin TimPushkin May 26, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI currently the restoring JVM does not record CRaCEngineOptions in restore-data at all, it and some other restore-settable options that affect only the restoring JVM/engine are filtered out here:

// Some restore-settable flags should not be updated in the restored JVM.
static bool should_record_for_restore(const JVMFlag& flag) {
precond(flag.is_restore_settable());
if (strncmp(flag.name(), "CRaCEngine", ARRAY_SIZE("CRaCEngine") - 1) == 0) {
assert(strcmp(flag.name(), "CRaCEngine") == 0 || strcmp(flag.name(), "CRaCEngineOptions") == 0,
"unexpected CRaCEngine* flag: %s", flag.name());
return false;
}
if (strcmp(flag.name(), "IgnoreUnrecognizedVMOptions") == 0) {
return false;
}
return true;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making the engine parse CRaCEngineOptions from restore-data is also not universal since the JVM can set additional options. A separate engine-internal mechanism would need to be implemented to make it pass info to its counterpart.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at the code, it seems I could use user_data to save the value of current argument of direct_map on each restore() call.
Extract the value once the get_checkpointable_status() is called.
WDYT?

@antonvoznia antonvoznia May 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also user_data is for passing from checkpoint to restore IIRC

can we extend the purpose of the mechanism? To avoid creating similar things a code generation for no reason

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe user_data has been unused for a while because we moved to more task-specific APIs, initially it was for CPU features AFAIR. So it should probably be deprecated and dropped rather than extended.

Anyway, relaying direct_map is an implementation detail of checkpointable_status in criuengine and should not touch user-facing APIs. It can use a similar implementation under the hood, just should not be user-visible.

@TimPushkin TimPushkin May 28, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refreshed my knowledge on user-data. It is used to add arbitrary data to checkpoint image, in criuengine case, by adding files to the image. This is suitable for checkpoint->restore data passing but not the other way around because a particular restore should not mutate an image possibly used for many restores.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as @TimPushkin suggested at the first message, does it make sense to look at process memory mapping in /proc/158992/map and find if there the same path as image_location has?
By this, we can verify

  1. if the process mapped directly
  2. if the new CheckpointTo path differs from RestoreFrom.

Do you guys think the approach makes sense or not?

@rvansa rvansa Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I've said before, I don't think that criuengine should do any introspection. If you need to pass data from restoring VM to restored one on JVM level, this would go to CracRestoreParameters. Since we're talking about something implemented on engine level, this means probably extending the implementation of crlib_restore_data_t::set_restore_data() to pass both JVM data and engine data.

Right now the implementation works this way:

  • restoring JVM creates a file at /known/path/crac.<restoring pid>
  • passes <restoring pid> to the engine as 4-byte (int) data through crlib_restore_data_t::set_restore_data()
  • engine sets the stringified version of this PID in CRAC_NEW_ARGS_ID env var
  • this is still visible by post-restore CRIU hook that uses this as the 4-byte payload to signal restored process
  • restored engine reads the value from signal, and makes this avaliable through crlib_restore_data_t::get_restore_data()
  • restored JVM now knows the PID of restoring process and reads /known/path/crac.<restoring pid>

The /known/path/... is modifiable. As Timofei said, we don't want to mutate checkpoint folder on restore, so this temporary path is preferred. Eventually this could work like this

  • restoring JVM passed arbitrary number of bytes (not just 4) to the engine
  • criuengine writes /known/path/<xxx>engine.<restoring pid>, combining JVM-level data and own data
  • passing the PID through CRAC_NEW_ARGS_ID and signals happens as before
  • restore engine reads /known/path/<xxx>engine.<restoring pid>, stores contents in an allocated buffer
  • JVM now reads its serialized data, does not touch any file at all

This approach will require modifications to engines that don't support arbitrary length of restore data, but I believe it is architecturally correct approach.


// With direct_map, restored memory pages are mmap'd directly from the
// checkpoint image file rather than copied into anonymous memory. A second
// checkpoint would overwrite that same image, corrupting the live mappings
// of the running process. CRIU has no safe way to re-checkpoint in this mode.
Comment on lines +504 to +507

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify: you can change CRaCCheckpointTo on restore and thus then checkpoint to another place saving the old image — some time ago I tested that this works and can be restored from, not sure if it still does though. But there seems to be no practical reason to do this, unless the second image is only the difference from the first one making the second checkpoint faster — BTW, are you sure this is not the case?

return CRLIB_CHECKPOINTABLE_NEVER;
}

const char* criuengine::get_criu() {
char* criu = _criu_location;
if (criu == nullptr) {
Expand Down Expand Up @@ -974,8 +987,17 @@ static crlib_user_data_t user_data_extension = {
destroy_user_data,
};

static crlib_checkpoint_availability_t checkpoint_availability_extension = {
{
CRLIB_EXTENSION_CHECKPOINT_AVAILABILITY_NAME,
sizeof(checkpoint_availability_extension)
},
get_checkpointable_status,
};

static const crlib_extension_t* extensions[] = {
&restore_data_extension.header,
&checkpoint_availability_extension.header,
&image_constraints_extension.header,
Comment on lines +1000 to 1001

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image constraints are always used on restore (which is expected to happen more often than checkpoint) so would be better to place the new extension beneath it. Same in simengine.

&image_score_extension.header,
&user_data_extension.header,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2026, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package jdk.internal.crac.mirror;

public enum CheckpointableStatus {
NEVER_AFTER_RESTORE, READY_LATER, READY;

public static CheckpointableStatus fromCode(int code) {
CheckpointableStatus[] values = values();
if (code < 0 || code >= values.length) {
throw new IllegalArgumentException("Unknown CheckpointableStatus code: " + code);
}
return values[code];
}
}
23 changes: 22 additions & 1 deletion src/java.base/share/classes/jdk/internal/crac/mirror/Core.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2025, Azul Systems, Inc. All rights reserved.
* Copyright (c) 2017, 2026, Azul Systems, Inc. All rights reserved.
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
Expand Down Expand Up @@ -57,6 +57,7 @@ public class Core {

private static final long JCMD_STREAM_NULL = 0;
private static native Object[] checkpointRestore0(int[] fdArr, Object[] objArr, boolean dryRun, long jcmdStream);
private static native int getCheckpointableStatus0();
private static final Object checkpointRestoreLock = new Object();
private static boolean checkpointInProgress = false;

Expand Down Expand Up @@ -272,6 +273,26 @@ public static void checkpointRestore() throws
private static void checkpointRestore(long jcmdStream) throws
CheckpointException,
RestoreException {
int status_code = getCheckpointableStatus0();
switch (CheckpointableStatus.fromCode(status_code)) {
case NEVER_AFTER_RESTORE -> {
CheckpointException ex = new CheckpointException();
ex.addSuppressed(new IllegalStateException("Current engine doesn't support second checkpoint after restore."));
throw ex;
}
case READY_LATER -> {
CheckpointException ex = new CheckpointException();
ex.addSuppressed(new IllegalStateException("CRaC cannot commit checkpoint right now, try later."));
throw ex;
}
case READY -> {
// fall through to checkpoint logic below
}
default -> {
System.err.printf("Engine returned unknown checkpointable status code: %d. Proceeding with checkpoint.%n", status_code);
Comment thread
TimPushkin marked this conversation as resolved.
}
}
Comment on lines +276 to +294

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we wanted to make this a public interface? I'm not sure if we should completely block checkpoint based on the current status since it can change until we actually reach calling the engine: resource processing below can take a long time (resources are free to block the thread).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we wanted to make this a public interface?

I'm not sure I caught the point.
If you are talking about CRaCMXBean, I didn't find a way to use it in the internal src/java.base/share/classes/jdk/internal/crac/mirror/Core.java.
These changes could go into CRaCImpl.java but then we have a problem with JCMD call.

'm not sure if we should completely block checkpoint based on the current status since it can change until we actually reach calling the engine

I could move this verification into synchronized block
synchronized (checkpointRestoreLock) {
But anyway, I would like to call it before the beforeCheckpoint() is handled.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could move this verification into synchronized block synchronized (checkpointRestoreLock) {

The race is between the Java code and the engine so synchronizing only on Java side would not help.

I still think we should leave calling this up to the user, while checkpointRestore should just attempt to checkpoint and fail if that is not possible.

If you are talking about CRaCMXBean, I didn't find a way to use it in the internal src/java.base/share/classes/jdk/internal/crac/mirror/Core.java.

CRaCMXBean could use Core if we want to call this in Core. Otherwise it should indeed be in CRaCImpl.


final List<String> newArguments;

// checkpointRestoreLock protects against the simultaneous
Expand Down
7 changes: 6 additions & 1 deletion src/java.base/share/native/libjava/CracCore.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2025, Azul Systems, Inc. All rights reserved.
* Copyright (c) 2017, 2026, Azul Systems, Inc. All rights reserved.
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
Expand Down Expand Up @@ -39,6 +39,11 @@ Java_jdk_internal_crac_mirror_Core_checkpointRestore0(JNIEnv *env, jclass ignore
return JVM_Checkpoint(env, fdArr, objArr, dry_run, jcmd_stream);
}

JNIEXPORT jint JNICALL
Java_jdk_internal_crac_mirror_Core_getCheckpointableStatus0(JNIEnv *env, jclass ignore) {
return JVM_GetCheckpointableStatus(env);
}

JNIEXPORT void JNICALL
Java_jdk_internal_crac_mirror_Core_startRecordingDecompilations0(JNIEnv *env, jclass ignore) {
JVM_StartRecordingDecompilations(env);
Expand Down
16 changes: 15 additions & 1 deletion src/java.base/share/native/libsimengine/simengine.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Azul Systems, Inc. All rights reserved.
* Copyright (c) 2021, 2026, Azul Systems, Inc. All rights reserved.
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
Expand Down Expand Up @@ -36,6 +36,7 @@
#endif // LINUX

#include "crcommon.hpp"
#include "crlib/crlib_checkpoint_availability.h"
#include "crlib/crlib_restore_data.h"
#include "crlib/crlib_description.h"
#include "jni.h"
Expand Down Expand Up @@ -288,8 +289,21 @@ static crlib_description_t description_extension = {
configuration_options,
};

static crlib_checkpointable_status_t get_checkpointable_status(crlib_conf_t *) {
return CRLIB_CHECKPOINTABLE_READY;
}

static crlib_checkpoint_availability checkpoint_availability_extension = {
{
CRLIB_EXTENSION_CHECKPOINT_AVAILABILITY_NAME,
sizeof(checkpoint_availability_extension)
},
get_checkpointable_status,
};

static const crlib_extension_t *extensions[] = {
&restore_data_extension.header,
&checkpoint_availability_extension.header,
&image_constraints_extension.header,
&image_score_extension.header,
&description_extension.header,
Expand Down