Path: blob/master/runtime/gc_realtime/RealtimeAccessBarrier.cpp
5986 views
/*******************************************************************************1* Copyright (c) 1991, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/2122#include "ArrayletObjectModel.hpp"23#include "Bits.hpp"24#include "EnvironmentRealtime.hpp"25#include "HeapRegionDescriptorRealtime.hpp"26#include "HeapRegionManager.hpp"27#include "JNICriticalRegion.hpp"28#include "RealtimeAccessBarrier.hpp"29#include "RealtimeGC.hpp"30#include "RealtimeMarkingScheme.hpp"3132#if defined(J9VM_GC_REALTIME)3334/**35* Static method for instantiating the access barrier.36*/37MM_RealtimeAccessBarrier *38MM_RealtimeAccessBarrier::newInstance(MM_EnvironmentBase *env)39{40MM_RealtimeAccessBarrier *barrier;4142barrier = (MM_RealtimeAccessBarrier *)env->getForge()->allocate(sizeof(MM_RealtimeAccessBarrier), MM_AllocationCategory::FIXED, OMR_GET_CALLSITE());43if (barrier) {44new(barrier) MM_RealtimeAccessBarrier(env);45if (!barrier->initialize(env)) {46barrier->kill(env);47barrier = NULL;48}49}50return barrier;51}5253bool54MM_RealtimeAccessBarrier::initialize(MM_EnvironmentBase *env)55{56if (!MM_ObjectAccessBarrier::initialize(env)) {57return false;58}59_realtimeGC = MM_GCExtensions::getExtensions(env)->realtimeGC;60_markingScheme = _realtimeGC->getMarkingScheme();6162return true;63}6465void66MM_RealtimeAccessBarrier::kill(MM_EnvironmentBase *env)67{68tearDown(env);69env->getForge()->free(this);70}7172void73MM_RealtimeAccessBarrier::tearDown(MM_EnvironmentBase *env)74{75MM_ObjectAccessBarrier::tearDown(env);76}7778/**79* Override of referenceGet. This barriered version does two things. (1) When the80* collector is tracing, it makes any gotten object "grey" to ensure that it is eventually81* traced. (2) When the collector is in the unmarkedImpliesCleared phase (after82* to-be-cleared soft and weak references have been identified and logically cleared), the83* get() operation returns NULL instead of the referent if the referent is unmarked.84*85* @param refObject the SoftReference or WeakReference object on which get() is being called.86* This barrier must not be called for PhantomReferences. The parameter must not be NULL.87*/88J9Object *89MM_RealtimeAccessBarrier::referenceGet(J9VMThread *vmThread, J9Object *refObject)90{91UDATA offset = J9VMJAVALANGREFREFERENCE_REFERENT_OFFSET(vmThread);92J9Object *referent = mixedObjectReadObject(vmThread, refObject, offset, false);9394/* Do nothing exceptional for NULL or marked referents */95if (referent == NULL) {96goto done;97}9899if (_markingScheme->isMarked(referent)) {100goto done;101}102103/* Now we know referent isn't NULL and isn't marked */104105if (_realtimeGC->getRealtimeDelegate()->_unmarkedImpliesCleared) {106/* In phase indicated by this flag, all unmarked references are logically cleared107* (will be physically cleared by the end of the gc).108*/109return NULL;110}111112/* Throughout tracing, we must turn any gotten reference into a root, because the113* thread doing the getting may already have been scanned. However, since we are114* running on a mutator thread and not a gc thread we do this indirectly by putting115* the object in the barrier buffer.116*/117if (_realtimeGC->isBarrierEnabled()) {118MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);119rememberObject(env, referent);120}121122done:123/* We must return the external reference */124return referent;125}126127void128MM_RealtimeAccessBarrier::referenceReprocess(J9VMThread *vmThread, J9Object *refObject)129{130referenceGet(vmThread, refObject);131}132133/**134* Barrier called from within j9jni_deleteGlobalRef to maintain the "double barrier"135* invariant (see comment in preObjectStore). To maintain EXACTLY what we do for ordinary136* reference stores, we should trap globalref creation for unscanned threads (only) and137* globalref deletion for all threads. But, since creating a global ref can138* only open a leak if that reference is subsequently deleted, it is sufficient to trap139* deletions for all threads. Note that we do not attempt to guard against JNI threads140* passing objects by back channels without creating global refs (we could not do so even141* if we wished to). Such JNI code is already treacherously unsafe and would sooner or142* later crash with or without incremental thread scanning.143*144* @param reference the JNI global reference that is about to be deleted. Must not be NULL145* (same requirement as j9jni_deleteGlobalRef).146*/147void148MM_RealtimeAccessBarrier::jniDeleteGlobalReference(J9VMThread *vmThread, J9Object *reference)149{150MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);151152if (!_realtimeGC->isBarrierEnabled()) {153return;154}155156deleteHeapReference(env, reference);157}158159/**160* Take any required action when a string constant has been fetched from the table.161* If there is a yield point between the last marking work and when the string table162* is cleared, then a thread could potentially get a reference to a string constant163* that is about to be cleared. This method prevents that be adding the string to164* the remembered set.165*/166void167MM_RealtimeAccessBarrier::stringConstantEscaped(J9VMThread *vmThread, J9Object *stringConst)168{169MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);170171if (_realtimeGC->isBarrierEnabled()) {172rememberObject(env, stringConst);173}174}175176/**177* Check that the two string constants should be considered truly "live".178* This is a bit of a hack to enable us to scan the string table incrementally.179* If we are still doing marking work, we treat a call to this method as meaning180* that one of the strings has been fetched from the string table. If we are finished181* marking work, but have yet to clear the string table, we treat any unmarked strings182* as cleared, by returning false from this function.183* NOTE: Because this function is called from the hash equals function, we can't184* actually tell which one of the strings is actually the one in the table already,185* so we have to assume they both are.186* @return true if they can be considered live, false otherwise.187*/188bool189MM_RealtimeAccessBarrier::checkStringConstantsLive(J9JavaVM *javaVM, j9object_t stringOne, j9object_t stringTwo)190{191if (_realtimeGC->isBarrierEnabled()) {192if (_realtimeGC->getRealtimeDelegate()->_unmarkedImpliesStringsCleared) {193/* If this flag is set, we will not scan the remembered set again, so we must194* treat any unmarked string constant as having been cleared.195*/196return (_realtimeGC->getMarkingScheme()->isMarked((J9Object *)stringOne) && _realtimeGC->getMarkingScheme()->isMarked((J9Object *)stringTwo));197} else {198J9VMThread* vmThread = javaVM->internalVMFunctions->currentVMThread(javaVM);199stringConstantEscaped(vmThread, (J9Object *)stringOne);200stringConstantEscaped(vmThread, (J9Object *)stringTwo);201}202}203return true;204}205206/**207* Equivalent to checkStringConstantsLive but for a single string constant208*/209bool210MM_RealtimeAccessBarrier::checkStringConstantLive(J9JavaVM *javaVM, j9object_t string)211{212if (_realtimeGC->isBarrierEnabled()) {213if (_realtimeGC->getRealtimeDelegate()->_unmarkedImpliesStringsCleared) {214/* If this flag is set, we will not scan the remembered set again, so we must215* treat any unmarked string constant as having been cleared.216*/217return _realtimeGC->getMarkingScheme()->isMarked((J9Object *)string);218} else {219J9VMThread* vmThread = javaVM->internalVMFunctions->currentVMThread(javaVM);220stringConstantEscaped(vmThread, (J9Object *)string);221}222}223return true;224}225226/**227* Take any required action when a heap reference is deleted. This method is called on228* all slots when finalizing a scoped object. It's also called from jniDeleteGlobalReference,229* but only when the collector is tracing (therefore we shouldn't check for isBarrierEnabled230* from this method).231*/232void233MM_RealtimeAccessBarrier::deleteHeapReference(MM_EnvironmentBase *env, J9Object *object)234{235rememberObject(env, object);236}237238/**239* DEBUG method. Only called when MM_GCExtensions::debugWriteBarrier >= 1.240*/241void242MM_RealtimeAccessBarrier::validateWriteBarrier(J9VMThread *vmThread, J9Object *dstObject, fj9object_t *dstAddress, J9Object *srcObject)243{244J9JavaVM *javaVM = vmThread->javaVM;245PORT_ACCESS_FROM_JAVAVM(javaVM);246bool const compressed = J9VMTHREAD_COMPRESS_OBJECT_REFERENCES(vmThread);247248switch(_extensions->objectModel.getScanType(dstObject)) {249case GC_ObjectModel::SCAN_MIXED_OBJECT_LINKED:250case GC_ObjectModel::SCAN_ATOMIC_MARKABLE_REFERENCE_OBJECT:251case GC_ObjectModel::SCAN_MIXED_OBJECT:252case GC_ObjectModel::SCAN_OWNABLESYNCHRONIZER_OBJECT:253case GC_ObjectModel::SCAN_CLASS_OBJECT:254case GC_ObjectModel::SCAN_CLASSLOADER_OBJECT:255case GC_ObjectModel::SCAN_REFERENCE_MIXED_OBJECT:256{257intptr_t slotIndex = GC_SlotObject::subtractSlotAddresses(dstAddress, (fj9object_t*)dstObject, compressed);258if (slotIndex < 0) {259j9tty_printf(PORTLIB, "validateWriteBarrier: slotIndex is negative dstAddress %d and dstObject %d\n", dstAddress, dstObject);260}261UDATA dataSizeInSlots = MM_Bits::convertBytesToSlots(_extensions->objectModel.getSizeInBytesWithHeader(dstObject));262if ((UDATA)slotIndex >= dataSizeInSlots) {263j9tty_printf(PORTLIB, "validateWriteBarrier: slotIndex (%d) >= object size in slots (%d)", slotIndex, dataSizeInSlots);264printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));265j9tty_printf(PORTLIB, "\n");266}267/* Also consider validating that slot is a ptr slot */268break;269}270271case GC_ObjectModel::SCAN_POINTER_ARRAY_OBJECT:272{273MM_HeapRegionManager *regionManager = MM_GCExtensions::getExtensions(javaVM)->getHeap()->getHeapRegionManager();274GC_ArrayletObjectModel::ArrayLayout layout = _extensions->indexableObjectModel.getArrayLayout((J9IndexableObject*)dstObject);275switch (layout) {276case GC_ArrayletObjectModel::InlineContiguous: {277UDATA** arrayletPtr = (UDATA**)(((J9IndexableObject*)dstObject) + 1);278UDATA* dataStart = *arrayletPtr;279UDATA* dataEnd = dataStart + _extensions->indexableObjectModel.getSizeInElements((J9IndexableObject*)dstObject);280if ((UDATA*)dstAddress < dataStart || (UDATA*)dstAddress >= dataEnd) {281j9tty_printf(PORTLIB, "validateWriteBarrier: IC: store to %p not in data section of array %p to %p", dstAddress, dataStart, dataEnd);282printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));283j9tty_printf(PORTLIB, "\n");284}285break;286}287case GC_ArrayletObjectModel::Discontiguous: {288MM_HeapRegionDescriptorRealtime *region = (MM_HeapRegionDescriptorRealtime *)regionManager->tableDescriptorForAddress(dstAddress);289if (!region->isArraylet()) {290j9tty_printf(PORTLIB, "validateWriteBarrier: D: dstAddress (%p) is not on an arraylet region", dstAddress);291printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));292j9tty_printf(PORTLIB, "\n");293}294else {295UDATA* arrayletParent = region->getArrayletParent(region->whichArraylet((UDATA*)dstAddress, javaVM->arrayletLeafLogSize));296if (arrayletParent != (UDATA*)dstObject) {297j9tty_printf(PORTLIB, "validateWriteBarrier: D: parent of arraylet (%p) is not destObject (%p)", arrayletParent, dstObject);298printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));299j9tty_printf(PORTLIB, "\n");300}301}302break;303}304case GC_ArrayletObjectModel::Hybrid: {305/* First check to see if it is in the last arraylet which is contiguous with the array spine. */306UDATA numberArraylets = _extensions->indexableObjectModel.numArraylets((J9IndexableObject*)dstObject);307UDATA** arrayletPtr = (UDATA**)(((J9IndexableObject*)dstObject)+1) + numberArraylets - 1;308UDATA* dataStart = *arrayletPtr;309UDATA spineSize = _extensions->indexableObjectModel.getSpineSize((J9IndexableObject*)dstObject);310UDATA* dataEnd = (UDATA*)(((U_8*)dstObject) + spineSize);311if ((UDATA*)dstAddress < dataStart || (UDATA*)dstAddress >= dataEnd) {312/* store was _not_ to last arraylet; attempt to validate that313* it was to one of the other arraylets of this array.314*/315MM_HeapRegionDescriptorRealtime *region = (MM_HeapRegionDescriptorRealtime *)regionManager->tableDescriptorForAddress(dstAddress);316if (!region->isArraylet()) {317j9tty_printf(PORTLIB, "validateWriteBarrier: H: dstAddress (%p) is not on an arraylet region", dstAddress);318printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));319}320else {321UDATA* arrayletParent = region->getArrayletParent(region->whichArraylet((UDATA*)dstAddress, javaVM->arrayletLeafLogSize));322if (arrayletParent != (UDATA*)dstObject) {323j9tty_printf(PORTLIB, "validateWriteBarrier: H: parent of arraylet (%p) is not destObject (%p)", arrayletParent, dstObject);324printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));325j9tty_printf(PORTLIB, "\n");326}327}328}329break;330}331default: {332j9tty_printf(PORTLIB, "validateWriteBarrier: unexpected arraylet type %d\n", layout);333assert(0);334}335};336break;337}338339case GC_ObjectModel::SCAN_PRIMITIVE_ARRAY_OBJECT:340j9tty_printf(PORTLIB, "validateWriteBarrier: writeBarrier called on array of primitive\n");341j9tty_printf(PORTLIB, "value being overwritten is %d\n", GC_SlotObject::readSlot(dstAddress, compressed));342printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));343j9tty_printf(PORTLIB, "\n");344break;345346default:347Assert_MM_unreachable();348}349}350351void352MM_RealtimeAccessBarrier::printClass(J9JavaVM *javaVM, J9Class* clazz)353{354J9ROMClass* romClass;355J9UTF8* utf;356PORT_ACCESS_FROM_JAVAVM(javaVM);357358/* TODO: In Sov, if the class is char[], the string is printed instead of the class name */359romClass = clazz->romClass;360if(romClass->modifiers & J9AccClassArray) {361J9ArrayClass* arrayClass = (J9ArrayClass*) clazz;362UDATA arity = arrayClass->arity;363utf = J9ROMCLASS_CLASSNAME(arrayClass->leafComponentType->romClass);364j9tty_printf(PORTLIB, "%.*s", (UDATA)J9UTF8_LENGTH(utf), J9UTF8_DATA(utf));365while(arity--) {366j9tty_printf(PORTLIB, "[]");367}368} else {369utf = J9ROMCLASS_CLASSNAME(romClass);370j9tty_printf(PORTLIB, "%.*s", (UDATA)J9UTF8_LENGTH(utf), J9UTF8_DATA(utf));371}372}373374/**375* Unmarked, heap reference, about to be deleted (or overwritten), while marking376* is in progress is to be remembered for later marking and scanning.377*/378void379MM_RealtimeAccessBarrier::rememberObject(MM_EnvironmentBase *env, J9Object *object)380{381if (_markingScheme->markObject(MM_EnvironmentRealtime::getEnvironment(env), object, true)) {382rememberObjectImpl(env, object);383}384}385386/**387* Read an object from an internal VM slot (J9VMThread, J9JavaVM, named field of J9Class).388* This function is only concerned with moving the actual data. Do not re-implement389* unless the value is stored in a non-native format (e.g. compressed object pointers).390* See readObjectFromInternalVMSlot() for higher-level actions.391* In realtime, we must remember the object being read in case it's being read from an392* unmarked thread and stored on a marked threads' stack. If the unmarked thread terminates393* before being marked, we will miss the object since a stack push doesn't invoke the write394* barrier.395* @param srcAddress the address of the field to be read396* @param isVolatile non-zero if the field is volatile, zero otherwise397*/398mm_j9object_t399MM_RealtimeAccessBarrier::readObjectFromInternalVMSlotImpl(J9VMThread *vmThread, j9object_t *srcAddress, bool isVolatile)400{401mm_j9object_t object = *srcAddress;402if (NULL != vmThread) {403rememberObjectIfBarrierEnabled(vmThread, object);404}405return object;406}407408/**409* Write an object to an internal VM slot (J9VMThread, J9JavaVM, named field of J9Class).410* In realtime, we must explicitly remember the value when being stored in case it's being411* read from an unmarked threads' stack and stored into a marked thread. If the unmarked412* thread terminates before being marked, we will miss the object since a stack pop doesn't413* invoke the barrier.414* @param destSlot the slot to be used415* @param value the value to be stored416*/417void418MM_RealtimeAccessBarrier::storeObjectToInternalVMSlot(J9VMThread *vmThread, J9Object** destSlot, J9Object *value)419{420if (preObjectStore(vmThread, destSlot, value, false)) {421rememberObjectIfBarrierEnabled(vmThread, value);422storeObjectToInternalVMSlotImpl(vmThread, destSlot, value, false);423postObjectStore(vmThread, destSlot, value, false);424}425}426427/**428* Call rememberObject() if realtimeGC->isBarrierEnabled() returns true.429* @param object the object to remember430*/431void432MM_RealtimeAccessBarrier::rememberObjectIfBarrierEnabled(J9VMThread *vmThread, J9Object* object)433{434MM_EnvironmentRealtime* env = MM_EnvironmentRealtime::getEnvironment(vmThread->omrVMThread);435if (_realtimeGC->isBarrierEnabled()) {436rememberObject(env, object);437}438}439440void*441MM_RealtimeAccessBarrier::jniGetPrimitiveArrayCritical(J9VMThread* vmThread, jarray array, jboolean *isCopy)442{443void *data = NULL;444J9JavaVM *javaVM = vmThread->javaVM;445J9InternalVMFunctions *functions = javaVM->internalVMFunctions;446447J9IndexableObject *arrayObject = (J9IndexableObject*)J9_JNI_UNWRAP_REFERENCE(array);448bool shouldCopy = false;449if((javaVM->runtimeFlags & J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) == J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) {450shouldCopy = true;451} else if (!_extensions->indexableObjectModel.isInlineContiguousArraylet(arrayObject)) {452/* an array having discontiguous extents is another reason to force the critical section to be a copy */453shouldCopy = true;454}455456if(shouldCopy) {457VM_VMAccess::inlineEnterVMFromJNI(vmThread);458GC_ArrayObjectModel* indexableObjectModel = &_extensions->indexableObjectModel;459I_32 sizeInElements = (I_32)indexableObjectModel->getSizeInElements(arrayObject);460UDATA sizeInBytes = indexableObjectModel->getDataSizeInBytes(arrayObject);461data = functions->jniArrayAllocateMemoryFromThread(vmThread, sizeInBytes);462if(NULL == data) {463functions->setNativeOutOfMemoryError(vmThread, 0, 0); // better error message here?464} else {465indexableObjectModel->memcpyFromArray(data, arrayObject, 0, sizeInElements);466if(NULL != isCopy) {467*isCopy = JNI_TRUE;468}469}470vmThread->jniCriticalCopyCount += 1;471VM_VMAccess::inlineExitVMToJNI(vmThread);472} else {473// acquire access and return a direct pointer474MM_JNICriticalRegion::enterCriticalRegion(vmThread, false);475data = (void *)_extensions->indexableObjectModel.getDataPointerForContiguous(arrayObject);476if(NULL != isCopy) {477*isCopy = JNI_FALSE;478}479}480return data;481}482483void484MM_RealtimeAccessBarrier::jniReleasePrimitiveArrayCritical(J9VMThread* vmThread, jarray array, void * elems, jint mode)485{486J9JavaVM *javaVM = vmThread->javaVM;487J9InternalVMFunctions *functions = javaVM->internalVMFunctions;488489J9IndexableObject *arrayObject = (J9IndexableObject*)J9_JNI_UNWRAP_REFERENCE(array);490bool shouldCopy = false;491if((javaVM->runtimeFlags & J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) == J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) {492shouldCopy = true;493} else if (!_extensions->indexableObjectModel.isInlineContiguousArraylet(arrayObject)) {494/* an array having discontiguous extents is another reason to force the critical section to be a copy */495shouldCopy = true;496}497498if(shouldCopy) {499VM_VMAccess::inlineEnterVMFromJNI(vmThread);500if(JNI_ABORT != mode) {501GC_ArrayObjectModel* indexableObjectModel = &_extensions->indexableObjectModel;502I_32 sizeInElements = (I_32)indexableObjectModel->getSizeInElements(arrayObject);503_extensions->indexableObjectModel.memcpyToArray(arrayObject, 0, sizeInElements, elems);504}505506// Commit means copy the data but do not free the buffer.507// All other modes free the buffer.508if(JNI_COMMIT != mode) {509functions->jniArrayFreeMemoryFromThread(vmThread, elems);510}511512if(vmThread->jniCriticalCopyCount > 0) {513vmThread->jniCriticalCopyCount -= 1;514} else {515Assert_MM_invalidJNICall();516}517518VM_VMAccess::inlineExitVMToJNI(vmThread);519} else {520/*521* Objects can not be moved if critical section is active522* This trace point will be generated if object has been moved or passed value of elems is corrupted523*/524void *data = (void *)_extensions->indexableObjectModel.getDataPointerForContiguous(arrayObject);525if(elems != data) {526Trc_MM_JNIReleasePrimitiveArrayCritical_invalid(vmThread, arrayObject, elems, data);527}528529MM_JNICriticalRegion::exitCriticalRegion(vmThread, false);530}531}532533const jchar*534MM_RealtimeAccessBarrier::jniGetStringCritical(J9VMThread* vmThread, jstring str, jboolean *isCopy)535{536jchar *data = NULL;537J9JavaVM *javaVM = vmThread->javaVM;538J9InternalVMFunctions *functions = javaVM->internalVMFunctions;539bool isCompressed = false;540bool shouldCopy = false;541bool hasVMAccess = false;542543/* For now only copying is supported for arraylets */544VM_VMAccess::inlineEnterVMFromJNI(vmThread);545hasVMAccess = true;546shouldCopy = true;547548if (shouldCopy) {549J9Object *stringObject = (J9Object*)J9_JNI_UNWRAP_REFERENCE(str);550J9IndexableObject *valueObject = (J9IndexableObject*)J9VMJAVALANGSTRING_VALUE(vmThread, stringObject);551jint length = J9VMJAVALANGSTRING_LENGTH(vmThread, stringObject);552UDATA sizeInBytes = length * sizeof(jchar);553554if (IS_STRING_COMPRESSED(vmThread, stringObject)) {555isCompressed = true;556}557data = (jchar*)functions->jniArrayAllocateMemoryFromThread(vmThread, sizeInBytes);558if (NULL == data) {559functions->setNativeOutOfMemoryError(vmThread, 0, 0); // better error message here?560} else {561GC_ArrayObjectModel* indexableObjectModel = &_extensions->indexableObjectModel;562if (isCompressed) {563jint i;564565for (i = 0; i < length; i++) {566data[i] = (jchar)(U_8)J9JAVAARRAYOFBYTE_LOAD(vmThread, (j9object_t)valueObject, i);567}568} else {569if (J9_ARE_ANY_BITS_SET(javaVM->runtimeFlags, J9_RUNTIME_STRING_BYTE_ARRAY)) {570// This API determines the stride based on the type of valueObject so in the [B case we must passin the length in bytes571indexableObjectModel->memcpyFromArray(data, valueObject, 0, (I_32)sizeInBytes);572} else {573indexableObjectModel->memcpyFromArray(data, valueObject, 0, length);574}575}576if (NULL != isCopy) {577*isCopy = JNI_TRUE;578}579}580vmThread->jniCriticalCopyCount += 1;581} else {582// acquire access and return a direct pointer583MM_JNICriticalRegion::enterCriticalRegion(vmThread, hasVMAccess);584J9Object *stringObject = (J9Object*)J9_JNI_UNWRAP_REFERENCE(str);585J9IndexableObject *valueObject = (J9IndexableObject*)J9VMJAVALANGSTRING_VALUE(vmThread, stringObject);586587data = (jchar*)_extensions->indexableObjectModel.getDataPointerForContiguous(valueObject);588589if (NULL != isCopy) {590*isCopy = JNI_FALSE;591}592}593if (hasVMAccess) {594VM_VMAccess::inlineExitVMToJNI(vmThread);595}596return data;597}598599void600MM_RealtimeAccessBarrier::jniReleaseStringCritical(J9VMThread* vmThread, jstring str, const jchar* elems)601{602J9JavaVM *javaVM = vmThread->javaVM;603J9InternalVMFunctions *functions = javaVM->internalVMFunctions;604bool hasVMAccess = false;605bool shouldCopy = false;606607/* For now only copying is supported for arraylets */608shouldCopy = true;609610if (shouldCopy) {611// String data is not copied back612functions->jniArrayFreeMemoryFromThread(vmThread, (void*)elems);613614if(vmThread->jniCriticalCopyCount > 0) {615vmThread->jniCriticalCopyCount -= 1;616} else {617Assert_MM_invalidJNICall();618}619} else {620// direct pointer, just drop access621MM_JNICriticalRegion::exitCriticalRegion(vmThread, hasVMAccess);622}623624if (hasVMAccess) {625VM_VMAccess::inlineExitVMToJNI(vmThread);626}627}628629#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)630bool631MM_RealtimeAccessBarrier::checkClassLive(J9JavaVM *javaVM, J9Class *classPtr)632{633J9ClassLoader *classLoader = classPtr->classLoader;634bool result = false;635636if ((0 == (classLoader->gcFlags & J9_GC_CLASS_LOADER_DEAD)) && (0 == (J9CLASS_FLAGS(classPtr) & J9AccClassDying))) {637/*638* this class has not been discovered dead yet639* so mark it if necessary to force it to be alive640*/641MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(javaVM);642MM_RealtimeGC *realtimeGC = extensions->realtimeGC;643J9Object *classLoaderObject = classLoader->classLoaderObject;644645if (NULL != classLoaderObject) {646if (realtimeGC->getRealtimeDelegate()->_unmarkedImpliesClasses) {647/*648* Mark is complete but GC cycle is still be in progress649* so we just can check is the correspondent class loader object marked650*/651result = realtimeGC->getMarkingScheme()->isMarked(classLoaderObject);652} else {653/*654* The return for this case is always true. If mark is active but not completed yet655* force this class to be marked to survive this GC656*/657658J9VMThread* vmThread = javaVM->internalVMFunctions->currentVMThread(javaVM);659rememberObjectIfBarrierEnabled(vmThread, classLoaderObject);660result = true;661}662} else {663/* this class loader probably is in initialization process and class loader object has not been attached yet */664result = true;665}666}667668return result;669}670#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */671672/**673* Unmarked, heap reference, about to be deleted (or overwritten), while marking674* is in progress is to be remembered for later marking and scanning.675* This method is called by MM_RealtimeAccessBarrier::rememberObject()676*/677void678MM_RealtimeAccessBarrier::rememberObjectImpl(MM_EnvironmentBase *env, J9Object* object)679{680J9VMThread *vmThread = (J9VMThread *)env->getLanguageVMThread();681MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(vmThread->javaVM);682683extensions->sATBBarrierRememberedSet->storeInFragment(env, &vmThread->sATBBarrierRememberedSetFragment, (UDATA *)object);684}685686void687MM_RealtimeAccessBarrier::forcedToFinalizableObject(J9VMThread* vmThread, J9Object* object)688{689MM_EnvironmentBase* env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);690if (isBarrierActive(env)) {691rememberObject(env, object);692}693}694695/**696* @copydoc MM_ObjectAccessBarrier::preObjectStore()697*698* This is the implementation of the realtime write barrier.699*700* Realtime uses a snapshot-at-the-beginning algorithm, but with a fuzzy snapshot in the701* sense that threads are allowed to run during the root scan. This requires a "double702* barrier." The barrier is active from the start of root scanning through the end of703* tracing. For an unscanned thread performing a store, the new value is remembered by704* the collector. For any thread performing a store (whether scanned or not), the old705* value is remembered by the collector before being overwritten (thus this barrier must be706* positioned as a pre-store barrier). For the latter ("Yuasa barrier") aspect of the707* double barrier, only the first overwritten value needs to be remembered (remembering708* others is harmless but not needed), and so we omit synchronization on the reading of the709* old value.710*/711bool712MM_RealtimeAccessBarrier::preObjectStoreInternal(J9VMThread *vmThread, J9Object *destObject, fj9object_t *destAddress, J9Object *value, bool isVolatile)713{714MM_EnvironmentBase* env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);715716if (isBarrierActive(env)) {717if (NULL != destObject) {718if (isDoubleBarrierActiveOnThread(vmThread)) {719rememberObject(env, value);720}721722J9Object *oldObject = NULL;723protectIfVolatileBefore(vmThread, isVolatile, true, false);724GC_SlotObject slotObject(vmThread->javaVM->omrVM, destAddress);725oldObject = slotObject.readReferenceFromSlot();726protectIfVolatileAfter(vmThread, isVolatile, true, false);727rememberObject(env, oldObject);728}729}730731return true;732}733734/**735* @copydoc MM_ObjectAccessBarrier::preObjectStore()736*737* This is the implementation of the realtime write barrier.738*739* Realtime uses a snapshot-at-the-beginning algorithm, but with a fuzzy snapshot in the740* sense that threads are allowed to run during the root scan. This requires a "double741* barrier." The barrier is active from the start of root scanning through the end of742* tracing. For an unscanned thread performing a store, the new value is remembered by743* the collector. For any thread performing a store (whether scanned or not), the old744* value is remembered by the collector before being overwritten (thus this barrier must be745* positioned as a pre-store barrier). For the latter ("Yuasa barrier") aspect of the746* double barrier, only the first overwritten value needs to be remembered (remembering747* others is harmless but not needed), and so we omit synchronization on the reading of the748* old value.749*/750bool751MM_RealtimeAccessBarrier::preObjectStoreInternal(J9VMThread *vmThread, J9Object **destAddress, J9Object *value, bool isVolatile)752{753MM_EnvironmentBase* env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);754755if (isBarrierActive(env)) {756if (isDoubleBarrierActiveOnThread(vmThread)) {757rememberObject(env, value);758}759J9Object* oldObject = NULL;760protectIfVolatileBefore(vmThread, isVolatile, true, false);761oldObject = *destAddress;762protectIfVolatileAfter(vmThread, isVolatile, true, false);763rememberObject(env, oldObject);764}765766return true;767}768769bool770MM_RealtimeAccessBarrier::preObjectStoreInternal(J9VMThread *vmThread, J9Object* destClass, J9Object **destAddress, J9Object *value, bool isVolatile)771{772/* the destClass argument is ignored, so just call the generic slot version */773return preObjectStoreInternal(vmThread, destAddress, value, isVolatile);774}775776/**777* @copydoc MM_ObjectAccessBarrier::preObjectStore()778*779* Metronome uses a snapshot-at-the-beginning algorithm, but with a fuzzy snapshot in the780* sense that threads are allowed to run during the root scan. This requires a "double781* barrier." The barrier is active from the start of root scanning through the end of782* tracing. For an unscanned thread performing a store, the new value is remembered by783* the collector. For any thread performing a store (whether scanned or not), the old784* value is remembered by the collector before being overwritten (thus this barrier must be785* positioned as a pre-store barrier). For the latter ("Yuasa barrier") aspect of the786* double barrier, only the first overwritten value needs to be remembered (remembering787* others is harmless but not needed), and so we omit synchronization on the reading of the788* old value.789**/790bool791MM_RealtimeAccessBarrier::preObjectStore(J9VMThread *vmThread, J9Object *destObject, fj9object_t *destAddress, J9Object *value, bool isVolatile)792{793return preObjectStoreInternal(vmThread, destObject, destAddress, value, isVolatile);794}795796/**797* @copydoc MM_MetronomeAccessBarrier::preObjectStore()798*799* Used for stores into classes800*/801bool802MM_RealtimeAccessBarrier::preObjectStore(J9VMThread *vmThread, J9Object *destClass, J9Object **destAddress, J9Object *value, bool isVolatile)803{804return preObjectStoreInternal(vmThread, destClass, destAddress, value, isVolatile);805}806807/**808* @copydoc MM_MetronomeAccessBarrier::preObjectStore()809*810* Used for stores into internal structures811*/812bool813MM_RealtimeAccessBarrier::preObjectStore(J9VMThread *vmThread, J9Object **destAddress, J9Object *value, bool isVolatile)814{815return preObjectStoreInternal(vmThread, destAddress, value, isVolatile);816}817818/**819* Enables the double barrier on the provided thread.820*/821void822MM_RealtimeAccessBarrier::setDoubleBarrierActiveOnThread(MM_EnvironmentBase* env)823{824MM_GCExtensions::getExtensions(env)->sATBBarrierRememberedSet->preserveLocalFragmentIndex(env, &(((J9VMThread *)env->getLanguageVMThread())->sATBBarrierRememberedSetFragment));825}826827/**828* Disables the double barrier on the provided thread.829*/830void831MM_RealtimeAccessBarrier::setDoubleBarrierInactiveOnThread(MM_EnvironmentBase* env)832{833MM_GCExtensions::getExtensions(env)->sATBBarrierRememberedSet->restoreLocalFragmentIndex(env, &(((J9VMThread *)env->getLanguageVMThread())->sATBBarrierRememberedSetFragment));834}835836void837MM_RealtimeAccessBarrier::initializeForNewThread(MM_EnvironmentBase* env)838{839MM_GCExtensions* extensions = MM_GCExtensions::getExtensions(env);840extensions->sATBBarrierRememberedSet->initializeFragment(env, &(((J9VMThread *)env->getLanguageVMThread())->sATBBarrierRememberedSetFragment));841if (isDoubleBarrierActive()) {842setDoubleBarrierActiveOnThread(env);843}844}845846/* TODO: meter this scanning and include into utilization tracking */847void848MM_RealtimeAccessBarrier::scanContiguousArray(MM_EnvironmentRealtime *env, J9IndexableObject *objectPtr)849{850bool const compressed = env->compressObjectReferences();851J9JavaVM *vm = (J9JavaVM *)env->getLanguageVM();852#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)853if(_realtimeGC->getRealtimeDelegate()->isDynamicClassUnloadingEnabled()) {854rememberObject(env, (J9Object *)objectPtr);855}856#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */857858/* if NUA is enabled, separate path for contiguous arrays */859fj9object_t *scanPtr = (fj9object_t*) _extensions->indexableObjectModel.getDataPointerForContiguous(objectPtr);860fj9object_t *endScanPtr = GC_SlotObject::addToSlotAddress(scanPtr, _extensions->indexableObjectModel.getSizeInElements(objectPtr), compressed);861862while(scanPtr < endScanPtr) {863/* since this is done from an external thread, we do not markObject, but rememberObject */864GC_SlotObject slotObject(vm->omrVM, scanPtr);865J9Object *field = slotObject.readReferenceFromSlot();866rememberObject(env, field);867scanPtr = GC_SlotObject::addToSlotAddress(scanPtr, 1, compressed);868}869/* this method assumes the array is large enough to set scan bit */870_markingScheme->setScanAtomic((J9Object *)objectPtr);871}872873bool874MM_RealtimeAccessBarrier::markAndScanContiguousArray(MM_EnvironmentRealtime *env, J9IndexableObject *objectPtr)875{876UDATA arrayletSize = _extensions->indexableObjectModel.arrayletSize(objectPtr, /* arraylet index */ 0);877878/* Sufficiently large to have a scan bit? */879if (arrayletSize < _extensions->minArraySizeToSetAsScanned) {880return false;881} else if (!_markingScheme->isScanned((J9Object *)objectPtr)) {882/* No, not scanned yet. We are going to mark it and scan right away */883_markingScheme->markAtomic((J9Object *)objectPtr);884/* The array might have been marked already (which means it will be scanned soon,885* or even being scanned at the moment). Regardless, we will proceed with scanning it */886scanContiguousArray(env, objectPtr);887}888889return true;890}891892/**893* Finds opportunities for doing the copy without executing Metronome WriteBarrier.894* @return ARRAY_COPY_SUCCESSFUL if copy was successful, ARRAY_COPY_NOT_DONE no copy is done895*/896I_32897MM_RealtimeAccessBarrier::backwardReferenceArrayCopyIndex(J9VMThread *vmThread, J9IndexableObject *srcObject, J9IndexableObject *destObject, I_32 srcIndex, I_32 destIndex, I_32 lengthInSlots)898{899MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(vmThread->omrVMThread);900901/* a high level caller ensured destObject == srcObject */902903if (_extensions->indexableObjectModel.isInlineContiguousArraylet(destObject)) {904905if (isBarrierActive(env)) {906907if (!markAndScanContiguousArray(env, destObject)) {908return ARRAY_COPY_NOT_DONE;909}910}911912return doCopyContiguousBackward(vmThread, srcObject, destObject, srcIndex, destIndex, lengthInSlots);913914}915916return -2;917}918919/**920* Finds opportunities for doing the copy without executing Metronome WriteBarrier.921* @return ARRAY_COPY_SUCCESSFUL if copy was successful, ARRAY_COPY_NOT_DONE no copy is done922*/923I_32924MM_RealtimeAccessBarrier::forwardReferenceArrayCopyIndex(J9VMThread *vmThread, J9IndexableObject *srcObject, J9IndexableObject *destObject, I_32 srcIndex, I_32 destIndex, I_32 lengthInSlots)925{926MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(vmThread->omrVMThread);927928if (_extensions->indexableObjectModel.isInlineContiguousArraylet(destObject)929&& _extensions->indexableObjectModel.isInlineContiguousArraylet(srcObject)) {930931if (isBarrierActive(env) ) {932933if ((destObject != srcObject) && isDoubleBarrierActiveOnThread(vmThread)) {934return ARRAY_COPY_NOT_DONE;935} else {936if (markAndScanContiguousArray(env, destObject)) {937return doCopyContiguousForward(vmThread, srcObject, destObject, srcIndex, destIndex, lengthInSlots);938}939}940941} else {942943return doCopyContiguousForward(vmThread, srcObject, destObject, srcIndex, destIndex, lengthInSlots);944945}946}947948return -2;949}950951#endif /* J9VM_GC_REALTIME */952953954955