Skip to content

Commit

Permalink
Merge pull request #17618 from JasonFengJ9/updatefjpparallelism
Browse files Browse the repository at this point in the history
CRIU resets j.l.VirtualThread.ForkJoinPool.parallelism after restore
  • Loading branch information
tajila committed Sep 12, 2023
2 parents 0990f59 + ac50591 commit dce0276
Show file tree
Hide file tree
Showing 9 changed files with 485 additions and 26 deletions.
23 changes: 23 additions & 0 deletions runtime/nls/j9vm/j9vm.nls
Expand Up @@ -2151,4 +2151,27 @@ J9NLS_VM_ILLEGAL_THREAD_STATE_UPCALL=The JVM failed to proceed due to the wrong
J9NLS_VM_ILLEGAL_THREAD_STATE_UPCALL.explanation=An error occurred when the JVM attempted to perform upcall in the trivial downcall
J9NLS_VM_ILLEGAL_THREAD_STATE_UPCALL.system_action=The JVM will throw a IllegalThreadStateException.
J9NLS_VM_ILLEGAL_THREAD_STATE_UPCALL.user_response=Ensure the specified linker options for downcall are valid.

J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_FORKJOINPOOL_CNF=The JVM could not reset java.lang.VirtualThread.ForkJoinPool.parallelism due to java.util.concurrent.ForkJoinPool class not found
# START NON-TRANSLATABLE
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_FORKJOINPOOL_CNF.sample_input_1=1
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_FORKJOINPOOL_CNF.explanation=CRIUSupport::checkpointJVM failed.
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_FORKJOINPOOL_CNF.system_action=The JVM will throw a JVMRestoreException.
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_FORKJOINPOOL_CNF.user_response=View CRIU documentation to determine how to resolve the exception.
# END NON-TRANSLATABLE

J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_DEFAULT_SCHEDULER_ADDRESS=The JVM could not reset java.lang.VirtualThread.ForkJoinPool.parallelism due to invalid DEFAULT_SCHEDULER address
# START NON-TRANSLATABLE
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_DEFAULT_SCHEDULER_ADDRESS.sample_input_1=1
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_DEFAULT_SCHEDULER_ADDRESS.explanation=CRIUSupport::checkpointJVM failed.
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_DEFAULT_SCHEDULER_ADDRESS.system_action=The JVM will throw a JVMRestoreException.
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_DEFAULT_SCHEDULER_ADDRESS.user_response=View CRIU documentation to determine how to resolve the exception.
# END NON-TRANSLATABLE

J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_PARALLELISM_OFFSET=The JVM could not reset java.lang.VirtualThread.ForkJoinPool.parallelism due to invalid parallelism offset
# START NON-TRANSLATABLE
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_PARALLELISM_OFFSET.sample_input_1=1
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_PARALLELISM_OFFSET.explanation=CRIUSupport::checkpointJVM failed.
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_PARALLELISM_OFFSET.system_action=The JVM will throw a JVMRestoreException.
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_PARALLELISM_OFFSET.user_response=View CRIU documentation to determine how to resolve the exception.
# END NON-TRANSLATABLE
95 changes: 95 additions & 0 deletions runtime/oti/VMHelpers.hpp
Expand Up @@ -40,6 +40,7 @@
#include "j9vmconstantpool.h"
#include "j9modifiers_api.h"
#include "j9cp.h"
#include "vm_api.h"
#include "ute.h"
#include "AtomicSupport.hpp"
#include "ObjectAllocationAPI.hpp"
Expand Down Expand Up @@ -2072,6 +2073,100 @@ class VM_VMHelpers
}
}
#endif /* JAVA_SPEC_VERSION >= 20 */

/**
* Get a static field object within a defining class.
*
* Current thread must have VM access.
*
* @param[in] currentThread the current J9VMThread
* @param[in] definingClassName the defining class name
* @param[in] fieldName the field name
* @param[in] signature the field signature
*
* @return the field object if successs, otherwise NULL
*/
static VMINLINE j9object_t
getStaticFieldObject(J9VMThread *currentThread, const char *definingClassName, const char *fieldName, const char *signature)
{
J9JavaVM *vm = currentThread->javaVM;
j9object_t fieldObject = NULL;
J9InternalVMFunctions const *vmFuncs = vm->internalVMFunctions;
J9Class *definingClass = vmFuncs->peekClassHashTable(currentThread, vm->systemClassLoader, (U_8 *)definingClassName, strlen(definingClassName));
void *fieldAddress = vmFuncs->staticFieldAddress(currentThread, definingClass, (U_8*)fieldName, strlen(fieldName), (U_8*)signature, strlen(signature), NULL, NULL, 0, NULL);
if (NULL != fieldAddress) {
MM_ObjectAccessBarrierAPI objectAccessBarrier = MM_ObjectAccessBarrierAPI(currentThread);
fieldObject = objectAccessBarrier.inlineStaticReadObject(currentThread, definingClass, (j9object_t*)fieldAddress, FALSE);
}
return fieldObject;
}

/**
* Find the offset of an instance field.
*
* @param[in] currentThread the current J9VMThread
* @param[in] instanceType the instance class
* @param[in] fieldName the field name
* @param[in] fieldSig the field signature
*
* @return the offset
*/
static VMINLINE IDATA
findinstanceFieldOffset(J9VMThread *currentThread, J9Class *instanceType, const char *fieldName, const char *fieldSig)
{
J9JavaVM *vm = currentThread->javaVM;

IDATA offset = (UDATA)vm->internalVMFunctions->instanceFieldOffset(
currentThread, instanceType,
(U_8 *)fieldName, strlen(fieldName),
(U_8 *)fieldSig, strlen(fieldSig),
NULL, NULL, 0);

if (-1 != offset) {
offset += J9VMTHREAD_OBJECT_HEADER_SIZE(currentThread);
}

return offset;
}

#if defined(J9VM_OPT_CRIU_SUPPORT)
/**
* Reset java.util.concurrent.ForkJoinPool.parallelism with a value supplied.
*
* Current thread must have VM access.
*
* @param[in] currentThread the current J9VMThread
* @param[in] instanceObject a java.util.concurrent.ForkJoinPool instance object
* @param[in] value the I_32 value to be set into the parallelism field
*
* @return true if the value has been set into the field within the instance object, false if not
*/
static VMINLINE bool
resetJUCForkJoinPoolParallelism(J9VMThread *currentThread, j9object_t instanceObject, I_32 value)
{
bool result = false;
J9JavaVM *vm = currentThread->javaVM;
IDATA fieldOffset = vm->checkpointState.jucForkJoinPoolParallelismOffset;

if (0 == fieldOffset) {
#define JUC_FORKJOINPOOL "java/util/concurrent/ForkJoinPool"
J9Class *definingClass = vm->internalVMFunctions->peekClassHashTable(currentThread, vm->systemClassLoader, (U_8 *)JUC_FORKJOINPOOL, LITERAL_STRLEN(JUC_FORKJOINPOOL));
#undef JUC_FORKJOINPOOL
if (NULL != definingClass) {
fieldOffset = findinstanceFieldOffset(currentThread, definingClass, "parallelism", "I");
} else {
fieldOffset = -1;
}
}
if (-1 != fieldOffset) {
MM_ObjectAccessBarrierAPI objectAccessBarrier = MM_ObjectAccessBarrierAPI(currentThread);
objectAccessBarrier.inlineMixedObjectStoreI32(currentThread, instanceObject, fieldOffset, value, false);
vm->checkpointState.jucForkJoinPoolParallelismOffset = fieldOffset;
result = true;
}
return result;
}
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
};

#endif /* VMHELPERS_HPP_ */
4 changes: 4 additions & 0 deletions runtime/oti/j9nonbuilder.h
Expand Up @@ -4202,6 +4202,9 @@ typedef struct J9DelayedLockingOpertionsRecord {

typedef struct J9CRIUCheckpointState {
U_32 flags;
#if JAVA_SPEC_VERSION >= 20
UDATA checkpointCPUCount;
#endif /* JAVA_SPEC_VERSION >= 20 */
struct J9DelayedLockingOpertionsRecord *delayedLockingOperationsRoot;
struct J9Pool *hookRecords;
struct J9Pool *classIterationRestoreHookRecords;
Expand Down Expand Up @@ -4238,6 +4241,7 @@ typedef struct J9CRIUCheckpointState {
UDATA libCRIUHandle;
struct J9VMInitArgs *restoreArgsList;
char *restoreArgsChars;
IDATA jucForkJoinPoolParallelismOffset;
} J9CRIUCheckpointState;
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */

Expand Down
66 changes: 45 additions & 21 deletions runtime/vm/CRIUHelpers.cpp
Expand Up @@ -46,7 +46,6 @@ J9_DECLARE_CONSTANT_UTF8(j9InternalCheckpointHookAPI_name, "org/eclipse/openj9/c
static void addInternalJVMCheckpointHook(J9VMThread *currentThread, BOOLEAN isRestore, J9Class *instanceType, BOOLEAN includeSubClass, hookFunc hookFunc);
static void cleanupCriuHooks(J9VMThread *currentThread);
static BOOLEAN fillinHookRecords(J9VMThread *currentThread, j9object_t object);
static IDATA findinstanceFieldOffsetHelper(J9VMThread *currentThread, J9Class *instanceType, const char *fieldName, const char *fieldSig);
static void initializeCriuHooks(J9VMThread *currentThread);
static BOOLEAN juRandomReseed(J9VMThread *currentThread, void *userData, const char **nlsMsgFormat);
static BOOLEAN criuRestoreInitializeTrace(J9VMThread *currentThread, void *userData, const char **nlsMsgFormat);
Expand Down Expand Up @@ -162,22 +161,6 @@ addInternalJVMCheckpointHook(J9VMThread *currentThread, BOOLEAN isRestore, J9Cla
}
}

static IDATA
findinstanceFieldOffsetHelper(J9VMThread *currentThread, J9Class *instanceType, const char *fieldName, const char *fieldSig)
{
IDATA offset = (UDATA)instanceFieldOffset(
currentThread, instanceType,
(U_8*)fieldName, strlen(fieldName),
(U_8*)fieldSig, strlen(fieldSig),
NULL, NULL, 0);

if (-1 != offset) {
offset += J9VMTHREAD_OBJECT_HEADER_SIZE(currentThread);
}

return offset;
}

/**
* An internal JVM checkpoint hook is to re-seed java.util.Random.seed.value.
*
Expand All @@ -197,13 +180,13 @@ juRandomReseed(J9VMThread *currentThread, void *userData, const char **nlsMsgFor
PORT_ACCESS_FROM_VMC(currentThread);

/* Assuming this hook record is to re-seed java.util.Random.seed.value. */
IDATA seedOffset = findinstanceFieldOffsetHelper(currentThread, hookRecord->instanceType, "seed", "Ljava/util/concurrent/atomic/AtomicLong;");
IDATA seedOffset = VM_VMHelpers::findinstanceFieldOffset(currentThread, hookRecord->instanceType, "seed", "Ljava/util/concurrent/atomic/AtomicLong;");
if (-1 != seedOffset) {
#define JUCA_ATOMICLONG "java/util/concurrent/atomic/AtomicLong"
J9Class *jucaAtomicLongClass = hashClassTableAt(vm->systemClassLoader, (U_8 *)JUCA_ATOMICLONG, LITERAL_STRLEN(JUCA_ATOMICLONG));
J9Class *jucaAtomicLongClass = peekClassHashTable(currentThread, vm->systemClassLoader, (U_8 *)JUCA_ATOMICLONG, LITERAL_STRLEN(JUCA_ATOMICLONG));
#undef JUCA_ATOMICLONG
if (NULL != jucaAtomicLongClass) {
IDATA valueOffset = findinstanceFieldOffsetHelper(currentThread, jucaAtomicLongClass, "value", "J");
IDATA valueOffset = VM_VMHelpers::findinstanceFieldOffset(currentThread, jucaAtomicLongClass, "value", "J");
if (-1 != valueOffset) {
PORT_ACCESS_FROM_JAVAVM(vm);
pool_state walkState = {0};
Expand Down Expand Up @@ -456,13 +439,31 @@ initializeCriuHooks(J9VMThread *currentThread)
}
}

#if JAVA_SPEC_VERSION >= 20
{
J9VMSystemProperty *vtParallelism = NULL;
PORT_ACCESS_FROM_VMC(currentThread);
if (J9SYSPROP_ERROR_NONE != getSystemProperty(vm, "jdk.virtualThreadScheduler.parallelism", &vtParallelism)) {
/* This system property only affects j.l.VirtualThread.ForkJoinPool.parallelism at VM startup. */
UDATA cpuCount = j9sysinfo_get_number_CPUs_by_type(J9PORT_CPU_TARGET);
if (cpuCount < 1) {
cpuCount = 1;
}
vm->checkpointState.checkpointCPUCount = cpuCount;
Trc_VM_criu_initializeCriuHooks_checkpointCPUCount(currentThread, cpuCount);
}
}
#endif /* JAVA_SPEC_VERSION >= 20 */

{
/* Add restore hook to re-seed java.uti.Random.seed.value */
#define JAVA_UTIL_RANDOM "java/util/Random"
J9Class *juRandomClass = peekClassHashTable(currentThread, vm->systemClassLoader, (U_8 *)JAVA_UTIL_RANDOM, LITERAL_STRLEN(JAVA_UTIL_RANDOM));
#undef JAVA_UTIL_RANDOM
if (NULL != juRandomClass) {
addInternalJVMCheckpointHook(currentThread, TRUE, juRandomClass, FALSE, juRandomReseed);
} else {
Trc_VM_criu_initializeCriuHooks_Random_CNF(currentThread);
}
addInternalJVMCheckpointHook(currentThread, TRUE, NULL, FALSE, criuRestoreInitializeTrace);
addInternalJVMCheckpointHook(currentThread, TRUE, NULL, FALSE, criuRestoreInitializeXrs);
Expand Down Expand Up @@ -495,8 +496,8 @@ fillinHookRecords(J9VMThread *currentThread, j9object_t object)
BOOLEAN result = TRUE;

J9InternalHookRecord *hookRecord = (J9InternalHookRecord*)pool_startDo(hookRecords, &walkState);
J9Class *clazz = J9OBJECT_CLAZZ_VM(vm, object);
while (NULL != hookRecord) {
J9Class *clazz = J9OBJECT_CLAZZ_VM(vm, object);
if ((clazz == hookRecord->instanceType)
|| (hookRecord->includeSubClass && isSameOrSuperClassOf(hookRecord->instanceType, clazz))
) {
Expand Down Expand Up @@ -602,6 +603,29 @@ runInternalJVMRestoreHooks(J9VMThread *currentThread, const char **nlsMsgFormat)
}
}

#if JAVA_SPEC_VERSION >= 20
if (0 != vm->checkpointState.checkpointCPUCount) {
/* Only reset j.l.VirtualThread.ForkJoinPool.parallelism if jdk.virtualThreadScheduler.parallelism is not set. */
PORT_ACCESS_FROM_VMC(currentThread);
UDATA cpuCount = j9sysinfo_get_number_CPUs_by_type(J9PORT_CPU_TARGET);
if (cpuCount < 1) {
/* This matches ForkJoinPool default constructor with Runtime.getRuntime().availableProcessors(). */
cpuCount = 1;
}
if (cpuCount != vm->checkpointState.checkpointCPUCount) {
j9object_t fjpObject = VM_VMHelpers::getStaticFieldObject(currentThread, "java/lang/VirtualThread", "DEFAULT_SCHEDULER", "Ljava/util/concurrent/ForkJoinPool;");
if (NULL != fjpObject) {
result = VM_VMHelpers::resetJUCForkJoinPoolParallelism(currentThread, fjpObject, cpuCount);
Trc_VM_criu_jlVirtualThreadForkJoinPoolResetParallelism_reset_parallelism(currentThread, fjpObject, cpuCount);
} else {
Trc_VM_criu_jlVirtualThreadForkJoinPoolResetParallelism_DEFAULT_SCHEDULER_NULL(currentThread);
}
} else {
Trc_VM_criu_jlVirtualThreadForkJoinPoolResetParallelism_same_cpucount(currentThread, cpuCount);
}
}
#endif /* JAVA_SPEC_VERSION >= 20 */

/* Cleanup at restore. */
cleanupCriuHooks(currentThread);
Trc_VM_criu_runRestoreHooks_Exit(currentThread);
Expand Down
6 changes: 6 additions & 0 deletions runtime/vm/j9vm.tdf
Expand Up @@ -930,3 +930,9 @@ TraceExit=Trc_VM_criu_initHooks_Exit Overhead=1 Level=5 Template="initializeCriu
TraceException=Trc_VM_criu_setSingleThreadModeJVMCRIUException_triggerOneOffJavaDump Overhead=1 Level=1 Template="setCRIUSingleThreadModeJVMCRIUException triggerOneOffDump() returns %d"

TraceException=Trc_VM_checkVisibility_Failed Overhead=1 Level=1 Template="checkVisibility from %p (%.*s) to %p (%.*s) modifiers=%zx lookupOptions=%zx failed"

TraceEvent=Trc_VM_criu_initializeCriuHooks_checkpointCPUCount Overhead=1 Level=3 Template="initializeCriuHooks() checkpoint CPU count (%zu)"
TraceEvent=Trc_VM_criu_jlVirtualThreadForkJoinPoolResetParallelism_same_cpucount Overhead=1 Level=5 Template="Reset j.l.VirtualThread.ForkJoinPool.parallelism: same cpu count (%zu) between C/R"
TraceEvent=Trc_VM_criu_jlVirtualThreadForkJoinPoolResetParallelism_reset_parallelism Overhead=1 Level=5 Template="Reset j.l.VirtualThread.ForkJoinPool.parallelism: fjpObject(0x%p) parallelism(%zu) after restore"
TraceException=Trc_VM_criu_initializeCriuHooks_Random_CNF Overhead=1 Level=3 Template="initializeCriuHooks() j.u.Random class not found"
TraceException=Trc_VM_criu_jlVirtualThreadForkJoinPoolResetParallelism_DEFAULT_SCHEDULER_NULL Overhead=1 Level=3 Template="Reset j.l.VirtualThread.ForkJoinPool.parallelism: DEFAULT_SCHEDULER is NULL"
25 changes: 20 additions & 5 deletions test/functional/cmdLineTests/criu/build.xml
Expand Up @@ -41,26 +41,41 @@
</target>

<target name="compile" depends="init" description="Using java ${JDK_VERSION} to compile the source ">
<property name="addExports" value="--add-exports java.base/openj9.internal.criu=ALL-UNNAMED --add-exports java.base/jdk.internal.misc=ALL-UNNAMED" />
<echo>===addExports: ${addExports}</echo>
<echo>Ant version is ${ant.version}</echo>
<echo>============COMPILER SETTINGS============</echo>
<echo>===fork: yes</echo>
<echo>===executable: ${compiler.javac}</echo>
<echo>===debug: on</echo>
<echo>===destdir: ${DEST}</echo>
<if>
<not>
<matches string="${JDK_VERSION}" pattern="^(8|9|1[0-9])$$" />
</not>
<then>
<property name="enablePreview" value="--enable-preview -source ${JDK_VERSION}" />
</then>
<else>
<property name="enablePreview" value="" />
<property name="excludeTestMachineResourceChange" value="org/openj9/criu/TestMachineResourceChange.java" />
</else>
</if>
<if>
<equals arg1="${JDK_VERSION}" arg2="8" />
<then>
<property name="excludeFile" value="org/openj9/criu/JDK11UpTimeoutAdjustmentTest.java" />
<property name="excludeJDK11UpTimeoutAdjustmentTest" value="org/openj9/criu/JDK11UpTimeoutAdjustmentTest.java" />
<javac srcdir="${src}" destdir="${build}" debug="true" fork="true" executable="${compiler.javac}" includeAntRuntime="false" encoding="ISO-8859-1">
<exclude name="${excludeFile}" />
<exclude name="${excludeJDK11UpTimeoutAdjustmentTest}" />
<exclude name="${excludeTestMachineResourceChange}" />
</javac>
</then>
<else>
<property name="addExports" value="--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED --add-exports=java.base/openj9.internal.criu=ALL-UNNAMED" />
<property name="addExports" value="--add-exports java.base/openj9.internal.criu=ALL-UNNAMED --add-exports java.base/jdk.internal.misc=ALL-UNNAMED" />
<echo>===addExports: ${addExports}</echo>
<echo>===enablePreview: ${enablePreview}</echo>
<javac srcdir="${src}" destdir="${build}" debug="true" fork="true" executable="${compiler.javac}" includeAntRuntime="false" encoding="ISO-8859-1">
<compilerarg line="${addExports}" />
<compilerarg line="${enablePreview}" />
<exclude name="${excludeTestMachineResourceChange}" />
</javac>
</else>
</if>
Expand Down

0 comments on commit dce0276

Please sign in to comment.