diff --git a/src/hotspot/share/include/crlib/crlib_checkpoint_availability.h b/src/hotspot/share/include/crlib/crlib_checkpoint_availability.h new file mode 100644 index 00000000000..962f7dd008e --- /dev/null +++ b/src/hotspot/share/include/crlib/crlib_checkpoint_availability.h @@ -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 + +#include "crlib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CRLIB_EXTENSION_CHECKPOINT_AVAILABILITY_NAME "checkpointable data" +#define CRLIB_EXTENSION_CHECKPOINTABLE(api) \ + CRLIB_EXTENSION(api, crlib_checkpoint_availability_t, CRLIB_EXTENSION_CHECKPOINT_CRLIB_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 + + +// 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 \ No newline at end of file diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 1a4bf363070..936d97fac96 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -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); diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 85e58caf0ae..638a267c955 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -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 diff --git a/src/hotspot/share/runtime/crac.cpp b/src/hotspot/share/runtime/crac.cpp index a9c8a6d0d75..f949c2f90d0 100644 --- a/src/hotspot/share/runtime/crac.cpp +++ b/src/hotspot/share/runtime/crac.cpp @@ -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 @@ -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) { + return status; + } else { + // Engine doesn't support checkpoint after restore, + // but it's a first checkpoint. So return READY. + return CRLIB_CHECKPOINTABLE_READY; + } + } + return status; + } + + return CRLIB_CHECKPOINTABLE_READY; +} + void VM_Crac::print_resources(const char* msg, ...) { if (CRaCPrintResourcesOnCheckpoint) { va_list ap; diff --git a/src/hotspot/share/runtime/crac.hpp b/src/hotspot/share/runtime/crac.hpp index d37a3aff1de..a15fd254ff0 100644 --- a/src/hotspot/share/runtime/crac.hpp +++ b/src/hotspot/share/runtime/crac.hpp @@ -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 @@ -57,6 +57,7 @@ class crac: AllStatic { static jlong restore_start_time(); static jlong uptime_since_restore(); + static jint checkpointable_status(); static jlong monotonic_time_offset() { return _javaTimeNanos_offset; diff --git a/src/hotspot/share/runtime/crac_engine.cpp b/src/hotspot/share/runtime/crac_engine.cpp index 2641a804ba8..ca3bd7282d4 100644 --- a/src/hotspot/share/runtime/crac_engine.cpp +++ b/src/hotspot/share/runtime/crac_engine.cpp @@ -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 @@ -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); +} diff --git a/src/hotspot/share/runtime/crac_engine.hpp b/src/hotspot/share/runtime/crac_engine.hpp index 5866cd8255c..b4f4b0a3270 100644 --- a/src/hotspot/share/runtime/crac_engine.hpp +++ b/src/hotspot/share/runtime/crac_engine.hpp @@ -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 @@ -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" @@ -79,6 +80,9 @@ class CracEngine : public CHeapObj { 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; @@ -90,6 +94,7 @@ class CracEngine : public CHeapObj { 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; }; diff --git a/src/java.base/linux/native/libcriuengine/criuengine.cpp b/src/java.base/linux/native/libcriuengine/criuengine.cpp index 7574085b248..27ba5eeea37 100644 --- a/src/java.base/linux/native/libcriuengine/criuengine.cpp +++ b/src/java.base/linux/native/libcriuengine/criuengine.cpp @@ -38,6 +38,7 @@ #include #include +#include "crlib/crlib_checkpoint_availability.h" #include "crlib/crlib_description.h" #include "crlib/crlib_restore_data.h" #include "crlib/crlib_user_data.h" @@ -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; + } + + // 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. + return CRLIB_CHECKPOINTABLE_NEVER; +} + const char* criuengine::get_criu() { char* criu = _criu_location; if (criu == nullptr) { @@ -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, &image_score_extension.header, &user_data_extension.header, diff --git a/src/java.base/share/classes/jdk/internal/crac/mirror/CheckpointableStatus.java b/src/java.base/share/classes/jdk/internal/crac/mirror/CheckpointableStatus.java new file mode 100644 index 00000000000..67e7dfe272d --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/crac/mirror/CheckpointableStatus.java @@ -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]; + } +} diff --git a/src/java.base/share/classes/jdk/internal/crac/mirror/Core.java b/src/java.base/share/classes/jdk/internal/crac/mirror/Core.java index 3442b9e2884..e7d2323b4a3 100644 --- a/src/java.base/share/classes/jdk/internal/crac/mirror/Core.java +++ b/src/java.base/share/classes/jdk/internal/crac/mirror/Core.java @@ -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. * @@ -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; @@ -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); + } + } + final List newArguments; // checkpointRestoreLock protects against the simultaneous diff --git a/src/java.base/share/native/libjava/CracCore.c b/src/java.base/share/native/libjava/CracCore.c index 2934e946af7..d4fd4fc8f02 100644 --- a/src/java.base/share/native/libjava/CracCore.c +++ b/src/java.base/share/native/libjava/CracCore.c @@ -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. * @@ -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); diff --git a/src/java.base/share/native/libsimengine/simengine.cpp b/src/java.base/share/native/libsimengine/simengine.cpp index 0387a6e6296..66823953cf7 100644 --- a/src/java.base/share/native/libsimengine/simengine.cpp +++ b/src/java.base/share/native/libsimengine/simengine.cpp @@ -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. * @@ -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" @@ -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,