Skip to content
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

Synchronization between continuation mounting and concurrent scanning #16290

Merged
merged 1 commit into from
Nov 14, 2022
Merged
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
2 changes: 1 addition & 1 deletion runtime/gc_glue_java/CompactSchemeFixupObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ MM_CompactSchemeFixupObject::fixupContinuationNativeSlots(MM_EnvironmentStandard
* mounted Virtual threads later during root fixup, we will skip it during this heap fixup pass
* (hence passing true for scanOnlyUnmounted parameter).
*/
if (VM_VMHelpers::needScanStacksForContinuation(currentThread, objectPtr, true)) {
if (VM_VMHelpers::needScanStacksForContinuation(currentThread, objectPtr)) {
StackIteratorData4CompactSchemeFixupObject localData;
localData.compactSchemeFixupObject = this;
localData.env = env;
Expand Down
3 changes: 2 additions & 1 deletion runtime/gc_glue_java/HeapWalkerDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,15 @@ void
MM_HeapWalkerDelegate::doContinuationNativeSlots(MM_EnvironmentBase *env, omrobjectptr_t objectPtr, MM_HeapWalkerSlotFunc function, void *userData)
{
J9VMThread *currentThread = (J9VMThread *)env->getLanguageVMThread();

if (VM_VMHelpers::needScanStacksForContinuation(currentThread, objectPtr)) {
StackIteratorData4HeapWalker localData;
localData.heapWalker = _heapWalker;
localData.env = env;
localData.fromObject = objectPtr;
localData.function = function;
localData.userData = userData;
/* so far there is no case we need ClassWalk for heapwalker, so we set bStackFrameClassWalkNeeded = false */
/* so far there is no case we need ClassWalk for heapwalker, so we set stackFrameClassWalkNeeded = false */
GC_VMThreadStackSlotIterator::scanSlots(currentThread, objectPtr, (void *)&localData, stackSlotIteratorForHeapWalker, false, false);
}
}
8 changes: 5 additions & 3 deletions runtime/gc_glue_java/MarkingDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,14 @@ MM_MarkingDelegate::scanContinuationNativeSlots(MM_EnvironmentBase *env, omrobje
localData.env = env;
localData.fromObject = objectPtr;

bool bStackFrameClassWalkNeeded = false;
bool stackFrameClassWalkNeeded = false;
#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
bStackFrameClassWalkNeeded = isDynamicClassUnloadingEnabled();
stackFrameClassWalkNeeded = isDynamicClassUnloadingEnabled();
#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */

GC_VMThreadStackSlotIterator::scanSlots(currentThread, objectPtr, (void *)&localData, stackSlotIteratorForMarkingDelegate, bStackFrameClassWalkNeeded, false);
/* In STW GC there are no racing carrier threads doing mount and no need for the synchronization. */
bool syncWithContinuationMounting = J9_ARE_ANY_BITS_SET(currentThread->privateFlags, J9_PRIVATE_FLAGS_CONCURRENT_MARK_ACTIVE);
GC_VMThreadStackSlotIterator::scanSlots(currentThread, objectPtr, (void *)&localData, stackSlotIteratorForMarkingDelegate, stackFrameClassWalkNeeded, false, syncWithContinuationMounting);
}
}

Expand Down
11 changes: 7 additions & 4 deletions runtime/gc_glue_java/MetronomeDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ MM_MetronomeDelegate::initialize(MM_EnvironmentBase *env)
if (NULL == accessBarrier) {
return false;
}
MM_GCExtensions::getExtensions(_javaVM)->accessBarrier = (MM_ObjectAccessBarrier *)accessBarrier;
_extensions->accessBarrier = (MM_ObjectAccessBarrier *)accessBarrier;

_javaVM->realtimeHeapMapBasePageRounded = _markingScheme->_markMap->getHeapMapBaseRegionRounded();
_javaVM->realtimeHeapMapBits = _markingScheme->_markMap->getHeapMapBits();
Expand Down Expand Up @@ -1653,12 +1653,15 @@ MM_MetronomeDelegate::scanContinuationNativeSlots(MM_EnvironmentRealtime *env, J
localData.env = env;
localData.fromObject = objectPtr;

bool bStackFrameClassWalkNeeded = false;
bool stackFrameClassWalkNeeded = false;
#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
bStackFrameClassWalkNeeded = isDynamicClassUnloadingEnabled();
stackFrameClassWalkNeeded = isDynamicClassUnloadingEnabled();
#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */

GC_VMThreadStackSlotIterator::scanSlots(currentThread, objectPtr, (void *)&localData, stackSlotIteratorForRealtimeGC, bStackFrameClassWalkNeeded, false);
/* In STW GC there are no racing carrier threads doing mount and no need for the synchronization. */
bool syncWithContinuationMounting = _realtimeGC->isCollectorConcurrentTracing();

GC_VMThreadStackSlotIterator::scanSlots(currentThread, objectPtr, (void *)&localData, stackSlotIteratorForRealtimeGC, stackFrameClassWalkNeeded, false, syncWithContinuationMounting);
}
}

Expand Down
4 changes: 3 additions & 1 deletion runtime/gc_glue_java/ScavengerDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,10 @@ MM_ScavengerDelegate::scanContinuationNativeSlots(MM_EnvironmentStandard *env, o
localData.env = env;
localData.reason = reason;
localData.shouldRemember = &shouldRemember;
/* In STW GC there are no racing carrier threads doing mount and no need for the synchronization. */
bool syncWithContinuationMounting = _extensions->isConcurrentScavengerInProgress();

GC_VMThreadStackSlotIterator::scanSlots(currentThread, objectPtr, (void *)&localData, stackSlotIteratorForScavenge, false, false);
GC_VMThreadStackSlotIterator::scanSlots(currentThread, objectPtr, (void *)&localData, stackSlotIteratorForScavenge, false, false, syncWithContinuationMounting);
}
return shouldRemember;
}
Expand Down
7 changes: 4 additions & 3 deletions runtime/gc_structs/VMThreadStackSlotIterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,14 @@ GC_VMThreadStackSlotIterator::scanSlots(
void *userData,
J9MODRON_OSLOTITERATOR *oSlotIterator,
bool includeStackFrameClassReferences,
bool trackVisibleFrameDepth
bool trackVisibleFrameDepth,
bool syncWithContinuationMounting
)
{
J9StackWalkState stackWalkState;
initializeStackWalkState(&stackWalkState, vmThread, userData, oSlotIterator, includeStackFrameClassReferences, trackVisibleFrameDepth);

VM_VMHelpers::walkContinuationStackFramesWrapper(vmThread, continuationObjectPtr, &stackWalkState);
initializeStackWalkState(&stackWalkState, vmThread, userData, oSlotIterator, includeStackFrameClassReferences, trackVisibleFrameDepth);
VM_VMHelpers::walkContinuationStackFramesWrapper(vmThread, continuationObjectPtr, &stackWalkState, syncWithContinuationMounting);
}

#if JAVA_SPEC_VERSION >= 19
Expand Down
3 changes: 2 additions & 1 deletion runtime/gc_structs/VMThreadStackSlotIterator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ class GC_VMThreadStackSlotIterator
void *userData,
J9MODRON_OSLOTITERATOR *oSlotIterator,
bool includeStackFrameClassReferences,
bool trackVisibleFrameDepth);
bool trackVisibleFrameDepth,
bool syncWithContinuationMounting = false);

#if JAVA_SPEC_VERSION >= 19
static void scanSlots(
Expand Down
6 changes: 3 additions & 3 deletions runtime/gc_vlhgc/CopyForwardScheme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2328,11 +2328,11 @@ MM_CopyForwardScheme::scanContinuationNativeSlots(MM_EnvironmentVLHGC *env, MM_A
localData.env = env;
localData.fromObject = objectPtr;
/* check _includeStackFrameClassReferences, _trackVisibleStackFrameDepth */
bool bStackFrameClassWalkNeeded = false;
bool stackFrameClassWalkNeeded = false;
#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
bStackFrameClassWalkNeeded = isDynamicClassUnloadingEnabled();
stackFrameClassWalkNeeded = isDynamicClassUnloadingEnabled();
#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */
GC_VMThreadStackSlotIterator::scanSlots(currentThread, objectPtr, (void *)&localData, stackSlotIteratorForCopyForwardScheme, bStackFrameClassWalkNeeded, false);
GC_VMThreadStackSlotIterator::scanSlots(currentThread, objectPtr, (void *)&localData, stackSlotIteratorForCopyForwardScheme, stackFrameClassWalkNeeded, false);
}
}

Expand Down
9 changes: 6 additions & 3 deletions runtime/gc_vlhgc/GlobalMarkingScheme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -797,12 +797,15 @@ MM_GlobalMarkingScheme::scanContinuationNativeSlots(MM_EnvironmentVLHGC *env, J9
localData.globalMarkingScheme = this;
localData.env = env;
localData.fromObject = objectPtr;
bool bStackFrameClassWalkNeeded = false;
bool stackFrameClassWalkNeeded = false;
#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
bStackFrameClassWalkNeeded = isDynamicClassUnloadingEnabled();
stackFrameClassWalkNeeded = isDynamicClassUnloadingEnabled();
#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */

Copy link
Contributor

@amicic amicic Nov 11, 2022

Choose a reason for hiding this comment

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

this can be either concurrent or STW and we should distinguish

Copy link
Contributor

Choose a reason for hiding this comment

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

look for _globalMarkIncrementType

GC_VMThreadStackSlotIterator::scanSlots(currentThread, objectPtr, (void *)&localData, stackSlotIteratorForGlobalMarkingScheme, bStackFrameClassWalkNeeded, false);
/* In STW GC there are no racing carrier threads doing mount and no need for the synchronization. */
bool syncWithContinuationMounting = (MM_VLHGCIncrementStats::mark_concurrent == static_cast<MM_CycleStateVLHGC*>(env->_cycleState)->_vlhgcIncrementStats._globalMarkIncrementType);

GC_VMThreadStackSlotIterator::scanSlots(currentThread, objectPtr, (void *)&localData, stackSlotIteratorForGlobalMarkingScheme, stackFrameClassWalkNeeded, syncWithContinuationMounting);
}
}

Expand Down
2 changes: 1 addition & 1 deletion runtime/gc_vlhgc/WriteOnceCompactor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1239,7 +1239,7 @@ MM_WriteOnceCompactor::fixupContinuationNativeSlots(MM_EnvironmentVLHGC* env, J9
* mounted Virtual threads later during root fixup, we will skip it during this heap fixup pass
* (hence passing true for scanOnlyUnmounted parameter).
*/
if (VM_VMHelpers::needScanStacksForContinuation(currentThread, objectPtr, true)) {
if (VM_VMHelpers::needScanStacksForContinuation(currentThread, objectPtr)) {
StackIteratorData4WriteOnceCompactor localData;
localData.writeOnceCompactor = this;
localData.env = env;
Expand Down
90 changes: 78 additions & 12 deletions runtime/oti/VMHelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "j9modifiers_api.h"
#include "j9cp.h"
#include "ute.h"
#include "AtomicSupport.hpp"
#include "ObjectAllocationAPI.hpp"

typedef enum {
Expand Down Expand Up @@ -2048,18 +2049,19 @@ class VM_VMHelpers
#endif /* JAVA_SPEC_VERSION > 11 */
}

Copy link
Contributor

Choose a reason for hiding this comment

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

A general comment about these Continuation specific helpers in this generic VMHelpers.hpp file....

Firstly, they should probably be in ContinuationHelpers.cpp file (or *.hpp for inline purposes) .
Secondly, J9VMContinuation should really be a class and all these helpers and (whether from VMHelpers or from ContinuationHelpers) should be class member methods. Most of those methods have J9VMContinuation as a parameter (or if they don't they would be a static methods).

But I guess, there is C style deeply rooted into most of VM structs, that I don't want to deal with (especially not as a part of these changes).

static VMINLINE UDATA
walkContinuationStackFramesWrapper(J9VMThread *vmThread, j9object_t continuationObject, J9StackWalkState *walkState)
{
UDATA rc = J9_STACKWALK_RC_NONE;
#if JAVA_SPEC_VERSION >= 19
J9VMContinuation *continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(vmThread, continuationObject);
rc = vmThread->javaVM->internalVMFunctions->walkContinuationStackFrames(vmThread, continuation, walkState);
#endif /* JAVA_SPEC_VERSION >= 19 */
return rc;
static VMINLINE J9VMThread *
getCarrierThreadFromContinuationState(uintptr_t continuationState)
{
return (J9VMThread *)(continuationState & (~(uintptr_t)J9_GC_CONTINUATION_STATE_CONCURRENT_SCAN));
}

static VMINLINE bool
isConcurrentlyScannedFromContinuationState(uintptr_t continuationState)
{
return J9_ARE_ANY_BITS_SET(continuationState, J9_GC_CONTINUATION_STATE_CONCURRENT_SCAN);
}

#if JAVA_SPEC_VERSION >= 19
/**
* Check if the related J9VMContinuation is mounted to carrier thread
* @param[in] continuation the related J9VMContinuation
Expand All @@ -2068,25 +2070,89 @@ class VM_VMHelpers
static VMINLINE bool
isContinuationMounted(J9VMContinuation *continuation)
{
return (NULL != continuation->carrierThread);
return J9_ARE_ANY_BITS_SET(continuation->state, ~(uintptr_t)J9_GC_CONTINUATION_STATE_CONCURRENT_SCAN);
}

static VMINLINE bool
isContinuationMountedOrConcurrentlyScanned(J9VMContinuation *continuation)
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider moving those 2 new helpers to more specific ContinuationHelper.hpp. As I said before, even the other existing ones that are continuation specific belong there, but for easier code review, it's probably better to leave them here.

Copy link
Contributor

@amicic amicic Nov 10, 2022

Choose a reason for hiding this comment

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

Consider same for any other NEW method in this file

{
return isContinuationMounted(continuation) || isConcurrentlyScannedFromContinuationState(continuation->state);
}

/*
* If low tagging failed due to either
*
* a carrier thread winning to mount, we don't need to do anything, since it will be compensated by pre/post mount actions
* another GC thread winning to scan, again don't do anything, and let the winning thread do the work, instead
*/
static VMINLINE bool
tryWinningConcurrentGCScan(J9VMContinuation *continuation)
{
return J9_GC_CONTINUATION_STATE_INITIAL == VM_AtomicSupport::lockCompareExchange(&continuation->state, J9_GC_CONTINUATION_STATE_INITIAL, J9_GC_CONTINUATION_STATE_CONCURRENT_SCAN);
}

static VMINLINE void
exitConcurrentGCScan(J9VMContinuation *continuation)
{
/* clear CONCURRENTSCANNING flag */
uintptr_t oldContinuationState = VM_AtomicSupport::bitAnd(&continuation->state, ~(uintptr_t)J9_GC_CONTINUATION_STATE_CONCURRENT_SCAN);
J9VMThread *carrierThread = getCarrierThreadFromContinuationState(oldContinuationState);
if (NULL != carrierThread) {
omrthread_monitor_enter(carrierThread->publicFlagsMutex);
/* notify the waiting carrierThread that we just finished scanning, and it can proceed with mounting. */
omrthread_monitor_notify_all(carrierThread->publicFlagsMutex);
omrthread_monitor_exit(carrierThread->publicFlagsMutex);
}
}
#endif /* JAVA_SPEC_VERSION >= 19 */

static VMINLINE UDATA
walkContinuationStackFramesWrapper(J9VMThread *vmThread, j9object_t continuationObject, J9StackWalkState *walkState, bool syncWithContinuationMounting)
{
UDATA rc = J9_STACKWALK_RC_NONE;
#if JAVA_SPEC_VERSION >= 19
J9VMContinuation *continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(vmThread, continuationObject);
if (syncWithContinuationMounting && (NULL != continuation)) {
if (!tryWinningConcurrentGCScan(continuation)) {
/* If continuation is mounted or already being scanned by another GC thread, we do nothing */
return rc;
Copy link
Contributor

Choose a reason for hiding this comment

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

GAC found it counterintuitive that failing to 'enter monitor' ends up returning 'success' (RC_NONE).
Besides, we don't have any explicit monitor, any more.
Perhaps a better name is tryWinning(Reserving?)ConcurrentGCScan? If so, the exit method should should be updated accordingly: exitConcurrentGCScan(Reservation?)

You could even repeat a lighter version of the fat comment associated with this method, just prior to return statement:
/* If continuation is mounted or already being scanned by another GC thread, we do nothing */

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this going to result in missing stack scans? A continuation always has a stack that needs walking, whether it's the stack for an unmounted continuation, or the stack of carrier thread for a mounted continuation.

Every continuation and every thread stack needs processing somewhere. I don't think it's valid to just do nothing in the case where the mount state is changing.

Copy link
Contributor

@amicic amicic Nov 11, 2022

Choose a reason for hiding this comment

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

Valid concern, but it should not result in missing scans. If continuation is not scanned by this GC thread now, it will be done during pre-mount or post-unmount (depending of concurrent style, SATB or incremental-update).
If pre-mount or post-mount does not occur during the concurrent phase, then scan will occur during initial STW GC increment root scan or during final STW increment when cleaning cards or re-scanning roots (again, depending on concurrent style)

This is why we use atomic: we must conclude that either we do here or it's done somewhere else, based on whether it's mounted or not. In this change we have to make sure this decision is made right (while we already made sure 'somewhere else' really does occur, as just explained).

Carrier thread are always scanned during root scanning, regardless if mounted something or not.

}
}
rc = vmThread->javaVM->internalVMFunctions->walkContinuationStackFrames(vmThread, continuation, walkState);
if (syncWithContinuationMounting && (NULL != continuation)) {
exitConcurrentGCScan(continuation);
}
#endif /* JAVA_SPEC_VERSION >= 19 */
return rc;
}

/**
* Check if we need to scan the java stack for the Continuation Object
Copy link
Contributor

Choose a reason for hiding this comment

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

Add a comment:
Used during main scan phase of GC (object graph traversal) or heap object iteration (in sliding compact). Not meant to be used during root scanning (neither strong roots nor weak roots)!

* Used during main scan phase of GC (object graph traversal) or heap object iteration (in sliding compact).
* Not meant to be used during root scanning (neither strong roots nor weak roots)!
* @param[in] vmThread the current J9VMThread
* @param[in] continuationObject the continuation object
* @param[in] scanOnlyUnmounted if it is true, only scan unmounted continuation object, default is false
* @return true if we need to scan the java stack
*/
static VMINLINE bool
needScanStacksForContinuation(J9VMThread *vmThread, j9object_t continuationObject, bool scanOnlyUnmounted = false)
needScanStacksForContinuation(J9VMThread *vmThread, j9object_t continuationObject)
{
bool needScan = false;
#if JAVA_SPEC_VERSION >= 19
jboolean started = J9VMJDKINTERNALVMCONTINUATION_STARTED(vmThread, continuationObject);
J9VMContinuation *continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(vmThread, continuationObject);
needScan = started && (NULL != continuation) && (!scanOnlyUnmounted || !isContinuationMounted(continuation));
/**
* We don't scan mounted continuations:
*
* for concurrent GCs, since stack is actively changing. Instead, we scan them during preMount or during root scanning if already mounted at cycle start or during postUnmount (might be indirectly via card cleaning) or during final STW (via root re-scan) if still mounted at cycle end
* for sliding compacts to avoid double slot fixups
*
* For fully STW GCs, there is no harm to scan them, but it's a waste of time since they are scanned during root scanning already.
*
* We don't scan currently scanned either - one scan is enough.
*/
needScan = started && (NULL != continuation) && (!isContinuationMountedOrConcurrentlyScanned(continuation));
Copy link
Contributor

@amicic amicic Nov 14, 2022

Choose a reason for hiding this comment

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

let's add a comment

We don't scan mounted continuations:

  • for concurrent GCs, since stack is actively changing. Instead, we scan them during preMount or during root scanning if already mounted at cycle start or during postUnmount (might be indirectly via card cleaning) or during final STW (via root re-scan) if still mounted at cycle end
  • for sliding compacts to avoid double slot fixups

For fully STW GCs, there is no harm to scan them, but it's a waste of time since they are scanned during root scanning already.

We don't scan currently scanned either - one scan is enough.

#endif /* JAVA_SPEC_VERSION >= 19 */
return needScan;
}
Expand Down
2 changes: 2 additions & 0 deletions runtime/oti/j9consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,8 @@ extern "C" {
#define J9_GC_MARK_MAP_LOG_SIZEOF_UDATA 0x5
#define J9_GC_MARK_MAP_UDATA_MASK 0x1F
#endif /* J9VM_ENV_DATA64 */
#define J9_GC_CONTINUATION_STATE_INITIAL 0
#define J9_GC_CONTINUATION_STATE_CONCURRENT_SCAN 0x1

#define J9VMGC_SIZECLASSES_MIN 0x1
#define J9VMGC_SIZECLASSES_MIN_SMALL 0x1
Expand Down
2 changes: 1 addition & 1 deletion runtime/oti/j9nonbuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -5003,7 +5003,7 @@ typedef struct J9VMContinuation {
struct J9JITGPRSpillArea jitGPRs;
struct J9I2JState i2jState;
struct J9VMEntryLocalStorage* oldEntryLocalStorage;
struct J9VMThread* carrierThread;
volatile UDATA state; /* it's a bit-wise struct of CarrierThread ID and ConcurrentlyScanned flag */
} J9VMContinuation;
#endif /* JAVA_SPEC_VERSION >= 19 */

Expand Down
45 changes: 40 additions & 5 deletions runtime/vm/ContinuationHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,44 @@ createContinuation(J9VMThread *currentThread, j9object_t continuationObject)
return result;
}

void
synchronizeWithConcurrentGCScan(J9VMThread *currentThread, J9VMContinuation *continuation)
{
volatile uintptr_t *localAddr = &continuation->state;
/* atomically 'or' (not 'set') continuation->state with currentThread */
uintptr_t oldContinuationState = VM_AtomicSupport::bitOr(localAddr, (uintptr_t)currentThread);

Assert_VM_Null(VM_VMHelpers::getCarrierThreadFromContinuationState(oldContinuationState));

if (VM_VMHelpers::isConcurrentlyScannedFromContinuationState(oldContinuationState)) {
/* currentThread was low tagged (GC was already in progress), but by 'or'-ing our ID, we let GC know there is a pending mount */
internalReleaseVMAccess(currentThread);

omrthread_monitor_enter(currentThread->publicFlagsMutex);
while (VM_VMHelpers::isConcurrentlyScannedFromContinuationState(*localAddr)) {
/* GC is still concurrently scanning the continuation(currentThread was still low tagged), wait for GC thread to notify us when it's done. */
omrthread_monitor_wait(currentThread->publicFlagsMutex);
}
omrthread_monitor_exit(currentThread->publicFlagsMutex);

internalAcquireVMAccess(currentThread);
}
}

BOOLEAN
enterContinuation(J9VMThread *currentThread, j9object_t continuationObject)
{
BOOLEAN result = TRUE;
jboolean started = J9VMJDKINTERNALVMCONTINUATION_STARTED(currentThread, continuationObject);
J9VMContinuation *continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(currentThread, continuationObject);

Assert_VM_Null(currentThread->currentContinuation);
Copy link
Contributor

@amicic amicic Nov 11, 2022

Choose a reason for hiding this comment

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

restore this assert
and assert that new continuation (that we got from the object) is not null

Assert_VM_notNull(continuation);

/* let GC know we are mounting, so they don't need to scan us, or if there is already ongoing scan wait till it's complete. */
synchronizeWithConcurrentGCScan(currentThread, continuation);
Copy link
Contributor

Choose a reason for hiding this comment

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

you can add a comment:
let GC know we are mounting, so they don't need to scan us, or if there is already ongoing scan wait till it's complete


VM_ContinuationHelpers::swapFieldsWithContinuation(currentThread, continuation, started);

continuation->carrierThread = currentThread;
currentThread->currentContinuation = continuation;

/* Reset counters which determine if the current continuation is pinned. */
Expand Down Expand Up @@ -141,19 +166,29 @@ yieldContinuation(J9VMThread *currentThread)
{
BOOLEAN result = TRUE;
J9VMContinuation *continuation = currentThread->currentContinuation;

Assert_VM_notNull(currentThread->currentContinuation);

VM_ContinuationHelpers::swapFieldsWithContinuation(currentThread, continuation);
continuation->carrierThread = NULL;
currentThread->currentContinuation = NULL;
VM_ContinuationHelpers::swapFieldsWithContinuation(currentThread, continuation);

/* We need a full fence here to preserve happens-before relationship on PPC and other weakly
* ordered architectures since learning/reservation is turned on by default. Since we have the
* global pin lock counters we only need to need to address yield points, as thats the
* only time a different virtualThread can run on the underlying j9vmthread.
*/
VM_AtomicSupport::readWriteBarrier();
/* we don't need atomic here, since no GC thread should be able to start scanning while continuation is mounted,
* nor should another carrier thread be able to mount before we complete the unmount (hence no risk to overwrite anything in a race).
* Order
*
* swap-stacks
* writeBarrier
* state initial
*
* must be maintained for weakly ordered CPUs, to unsure that once the continuation is again available for GC scan (on potentially remote CPUs), all CPUs see up-to-date stack .
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

@LinHu2016, you can expand this comment with:

Order 

swap-stacks
writeBarrier
state initial

must be maintained for weakly ordered CPUs, to unsure that once the continuation is again available for GC scan (on potentially remote CPUs), all CPUs see up-to-date stack .

Assert_VM_true((uintptr_t)currentThread == continuation->state);
continuation->state = J9_GC_CONTINUATION_STATE_INITIAL;
Copy link
Contributor

@amicic amicic Nov 11, 2022

Choose a reason for hiding this comment

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

assert before this that carrier ID in the state is current thread


return result;
}
Expand Down