Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/gc_realtime/RealtimeAccessBarrier.cpp
5986 views
1
/*******************************************************************************
2
* Copyright (c) 1991, 2021 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* 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-exception
21
*******************************************************************************/
22
23
#include "ArrayletObjectModel.hpp"
24
#include "Bits.hpp"
25
#include "EnvironmentRealtime.hpp"
26
#include "HeapRegionDescriptorRealtime.hpp"
27
#include "HeapRegionManager.hpp"
28
#include "JNICriticalRegion.hpp"
29
#include "RealtimeAccessBarrier.hpp"
30
#include "RealtimeGC.hpp"
31
#include "RealtimeMarkingScheme.hpp"
32
33
#if defined(J9VM_GC_REALTIME)
34
35
/**
36
* Static method for instantiating the access barrier.
37
*/
38
MM_RealtimeAccessBarrier *
39
MM_RealtimeAccessBarrier::newInstance(MM_EnvironmentBase *env)
40
{
41
MM_RealtimeAccessBarrier *barrier;
42
43
barrier = (MM_RealtimeAccessBarrier *)env->getForge()->allocate(sizeof(MM_RealtimeAccessBarrier), MM_AllocationCategory::FIXED, OMR_GET_CALLSITE());
44
if (barrier) {
45
new(barrier) MM_RealtimeAccessBarrier(env);
46
if (!barrier->initialize(env)) {
47
barrier->kill(env);
48
barrier = NULL;
49
}
50
}
51
return barrier;
52
}
53
54
bool
55
MM_RealtimeAccessBarrier::initialize(MM_EnvironmentBase *env)
56
{
57
if (!MM_ObjectAccessBarrier::initialize(env)) {
58
return false;
59
}
60
_realtimeGC = MM_GCExtensions::getExtensions(env)->realtimeGC;
61
_markingScheme = _realtimeGC->getMarkingScheme();
62
63
return true;
64
}
65
66
void
67
MM_RealtimeAccessBarrier::kill(MM_EnvironmentBase *env)
68
{
69
tearDown(env);
70
env->getForge()->free(this);
71
}
72
73
void
74
MM_RealtimeAccessBarrier::tearDown(MM_EnvironmentBase *env)
75
{
76
MM_ObjectAccessBarrier::tearDown(env);
77
}
78
79
/**
80
* Override of referenceGet. This barriered version does two things. (1) When the
81
* collector is tracing, it makes any gotten object "grey" to ensure that it is eventually
82
* traced. (2) When the collector is in the unmarkedImpliesCleared phase (after
83
* to-be-cleared soft and weak references have been identified and logically cleared), the
84
* get() operation returns NULL instead of the referent if the referent is unmarked.
85
*
86
* @param refObject the SoftReference or WeakReference object on which get() is being called.
87
* This barrier must not be called for PhantomReferences. The parameter must not be NULL.
88
*/
89
J9Object *
90
MM_RealtimeAccessBarrier::referenceGet(J9VMThread *vmThread, J9Object *refObject)
91
{
92
UDATA offset = J9VMJAVALANGREFREFERENCE_REFERENT_OFFSET(vmThread);
93
J9Object *referent = mixedObjectReadObject(vmThread, refObject, offset, false);
94
95
/* Do nothing exceptional for NULL or marked referents */
96
if (referent == NULL) {
97
goto done;
98
}
99
100
if (_markingScheme->isMarked(referent)) {
101
goto done;
102
}
103
104
/* Now we know referent isn't NULL and isn't marked */
105
106
if (_realtimeGC->getRealtimeDelegate()->_unmarkedImpliesCleared) {
107
/* In phase indicated by this flag, all unmarked references are logically cleared
108
* (will be physically cleared by the end of the gc).
109
*/
110
return NULL;
111
}
112
113
/* Throughout tracing, we must turn any gotten reference into a root, because the
114
* thread doing the getting may already have been scanned. However, since we are
115
* running on a mutator thread and not a gc thread we do this indirectly by putting
116
* the object in the barrier buffer.
117
*/
118
if (_realtimeGC->isBarrierEnabled()) {
119
MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);
120
rememberObject(env, referent);
121
}
122
123
done:
124
/* We must return the external reference */
125
return referent;
126
}
127
128
void
129
MM_RealtimeAccessBarrier::referenceReprocess(J9VMThread *vmThread, J9Object *refObject)
130
{
131
referenceGet(vmThread, refObject);
132
}
133
134
/**
135
* Barrier called from within j9jni_deleteGlobalRef to maintain the "double barrier"
136
* invariant (see comment in preObjectStore). To maintain EXACTLY what we do for ordinary
137
* reference stores, we should trap globalref creation for unscanned threads (only) and
138
* globalref deletion for all threads. But, since creating a global ref can
139
* only open a leak if that reference is subsequently deleted, it is sufficient to trap
140
* deletions for all threads. Note that we do not attempt to guard against JNI threads
141
* passing objects by back channels without creating global refs (we could not do so even
142
* if we wished to). Such JNI code is already treacherously unsafe and would sooner or
143
* later crash with or without incremental thread scanning.
144
*
145
* @param reference the JNI global reference that is about to be deleted. Must not be NULL
146
* (same requirement as j9jni_deleteGlobalRef).
147
*/
148
void
149
MM_RealtimeAccessBarrier::jniDeleteGlobalReference(J9VMThread *vmThread, J9Object *reference)
150
{
151
MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);
152
153
if (!_realtimeGC->isBarrierEnabled()) {
154
return;
155
}
156
157
deleteHeapReference(env, reference);
158
}
159
160
/**
161
* Take any required action when a string constant has been fetched from the table.
162
* If there is a yield point between the last marking work and when the string table
163
* is cleared, then a thread could potentially get a reference to a string constant
164
* that is about to be cleared. This method prevents that be adding the string to
165
* the remembered set.
166
*/
167
void
168
MM_RealtimeAccessBarrier::stringConstantEscaped(J9VMThread *vmThread, J9Object *stringConst)
169
{
170
MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);
171
172
if (_realtimeGC->isBarrierEnabled()) {
173
rememberObject(env, stringConst);
174
}
175
}
176
177
/**
178
* Check that the two string constants should be considered truly "live".
179
* This is a bit of a hack to enable us to scan the string table incrementally.
180
* If we are still doing marking work, we treat a call to this method as meaning
181
* that one of the strings has been fetched from the string table. If we are finished
182
* marking work, but have yet to clear the string table, we treat any unmarked strings
183
* as cleared, by returning false from this function.
184
* NOTE: Because this function is called from the hash equals function, we can't
185
* actually tell which one of the strings is actually the one in the table already,
186
* so we have to assume they both are.
187
* @return true if they can be considered live, false otherwise.
188
*/
189
bool
190
MM_RealtimeAccessBarrier::checkStringConstantsLive(J9JavaVM *javaVM, j9object_t stringOne, j9object_t stringTwo)
191
{
192
if (_realtimeGC->isBarrierEnabled()) {
193
if (_realtimeGC->getRealtimeDelegate()->_unmarkedImpliesStringsCleared) {
194
/* If this flag is set, we will not scan the remembered set again, so we must
195
* treat any unmarked string constant as having been cleared.
196
*/
197
return (_realtimeGC->getMarkingScheme()->isMarked((J9Object *)stringOne) && _realtimeGC->getMarkingScheme()->isMarked((J9Object *)stringTwo));
198
} else {
199
J9VMThread* vmThread = javaVM->internalVMFunctions->currentVMThread(javaVM);
200
stringConstantEscaped(vmThread, (J9Object *)stringOne);
201
stringConstantEscaped(vmThread, (J9Object *)stringTwo);
202
}
203
}
204
return true;
205
}
206
207
/**
208
* Equivalent to checkStringConstantsLive but for a single string constant
209
*/
210
bool
211
MM_RealtimeAccessBarrier::checkStringConstantLive(J9JavaVM *javaVM, j9object_t string)
212
{
213
if (_realtimeGC->isBarrierEnabled()) {
214
if (_realtimeGC->getRealtimeDelegate()->_unmarkedImpliesStringsCleared) {
215
/* If this flag is set, we will not scan the remembered set again, so we must
216
* treat any unmarked string constant as having been cleared.
217
*/
218
return _realtimeGC->getMarkingScheme()->isMarked((J9Object *)string);
219
} else {
220
J9VMThread* vmThread = javaVM->internalVMFunctions->currentVMThread(javaVM);
221
stringConstantEscaped(vmThread, (J9Object *)string);
222
}
223
}
224
return true;
225
}
226
227
/**
228
* Take any required action when a heap reference is deleted. This method is called on
229
* all slots when finalizing a scoped object. It's also called from jniDeleteGlobalReference,
230
* but only when the collector is tracing (therefore we shouldn't check for isBarrierEnabled
231
* from this method).
232
*/
233
void
234
MM_RealtimeAccessBarrier::deleteHeapReference(MM_EnvironmentBase *env, J9Object *object)
235
{
236
rememberObject(env, object);
237
}
238
239
/**
240
* DEBUG method. Only called when MM_GCExtensions::debugWriteBarrier >= 1.
241
*/
242
void
243
MM_RealtimeAccessBarrier::validateWriteBarrier(J9VMThread *vmThread, J9Object *dstObject, fj9object_t *dstAddress, J9Object *srcObject)
244
{
245
J9JavaVM *javaVM = vmThread->javaVM;
246
PORT_ACCESS_FROM_JAVAVM(javaVM);
247
bool const compressed = J9VMTHREAD_COMPRESS_OBJECT_REFERENCES(vmThread);
248
249
switch(_extensions->objectModel.getScanType(dstObject)) {
250
case GC_ObjectModel::SCAN_MIXED_OBJECT_LINKED:
251
case GC_ObjectModel::SCAN_ATOMIC_MARKABLE_REFERENCE_OBJECT:
252
case GC_ObjectModel::SCAN_MIXED_OBJECT:
253
case GC_ObjectModel::SCAN_OWNABLESYNCHRONIZER_OBJECT:
254
case GC_ObjectModel::SCAN_CLASS_OBJECT:
255
case GC_ObjectModel::SCAN_CLASSLOADER_OBJECT:
256
case GC_ObjectModel::SCAN_REFERENCE_MIXED_OBJECT:
257
{
258
intptr_t slotIndex = GC_SlotObject::subtractSlotAddresses(dstAddress, (fj9object_t*)dstObject, compressed);
259
if (slotIndex < 0) {
260
j9tty_printf(PORTLIB, "validateWriteBarrier: slotIndex is negative dstAddress %d and dstObject %d\n", dstAddress, dstObject);
261
}
262
UDATA dataSizeInSlots = MM_Bits::convertBytesToSlots(_extensions->objectModel.getSizeInBytesWithHeader(dstObject));
263
if ((UDATA)slotIndex >= dataSizeInSlots) {
264
j9tty_printf(PORTLIB, "validateWriteBarrier: slotIndex (%d) >= object size in slots (%d)", slotIndex, dataSizeInSlots);
265
printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));
266
j9tty_printf(PORTLIB, "\n");
267
}
268
/* Also consider validating that slot is a ptr slot */
269
break;
270
}
271
272
case GC_ObjectModel::SCAN_POINTER_ARRAY_OBJECT:
273
{
274
MM_HeapRegionManager *regionManager = MM_GCExtensions::getExtensions(javaVM)->getHeap()->getHeapRegionManager();
275
GC_ArrayletObjectModel::ArrayLayout layout = _extensions->indexableObjectModel.getArrayLayout((J9IndexableObject*)dstObject);
276
switch (layout) {
277
case GC_ArrayletObjectModel::InlineContiguous: {
278
UDATA** arrayletPtr = (UDATA**)(((J9IndexableObject*)dstObject) + 1);
279
UDATA* dataStart = *arrayletPtr;
280
UDATA* dataEnd = dataStart + _extensions->indexableObjectModel.getSizeInElements((J9IndexableObject*)dstObject);
281
if ((UDATA*)dstAddress < dataStart || (UDATA*)dstAddress >= dataEnd) {
282
j9tty_printf(PORTLIB, "validateWriteBarrier: IC: store to %p not in data section of array %p to %p", dstAddress, dataStart, dataEnd);
283
printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));
284
j9tty_printf(PORTLIB, "\n");
285
}
286
break;
287
}
288
case GC_ArrayletObjectModel::Discontiguous: {
289
MM_HeapRegionDescriptorRealtime *region = (MM_HeapRegionDescriptorRealtime *)regionManager->tableDescriptorForAddress(dstAddress);
290
if (!region->isArraylet()) {
291
j9tty_printf(PORTLIB, "validateWriteBarrier: D: dstAddress (%p) is not on an arraylet region", dstAddress);
292
printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));
293
j9tty_printf(PORTLIB, "\n");
294
}
295
else {
296
UDATA* arrayletParent = region->getArrayletParent(region->whichArraylet((UDATA*)dstAddress, javaVM->arrayletLeafLogSize));
297
if (arrayletParent != (UDATA*)dstObject) {
298
j9tty_printf(PORTLIB, "validateWriteBarrier: D: parent of arraylet (%p) is not destObject (%p)", arrayletParent, dstObject);
299
printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));
300
j9tty_printf(PORTLIB, "\n");
301
}
302
}
303
break;
304
}
305
case GC_ArrayletObjectModel::Hybrid: {
306
/* First check to see if it is in the last arraylet which is contiguous with the array spine. */
307
UDATA numberArraylets = _extensions->indexableObjectModel.numArraylets((J9IndexableObject*)dstObject);
308
UDATA** arrayletPtr = (UDATA**)(((J9IndexableObject*)dstObject)+1) + numberArraylets - 1;
309
UDATA* dataStart = *arrayletPtr;
310
UDATA spineSize = _extensions->indexableObjectModel.getSpineSize((J9IndexableObject*)dstObject);
311
UDATA* dataEnd = (UDATA*)(((U_8*)dstObject) + spineSize);
312
if ((UDATA*)dstAddress < dataStart || (UDATA*)dstAddress >= dataEnd) {
313
/* store was _not_ to last arraylet; attempt to validate that
314
* it was to one of the other arraylets of this array.
315
*/
316
MM_HeapRegionDescriptorRealtime *region = (MM_HeapRegionDescriptorRealtime *)regionManager->tableDescriptorForAddress(dstAddress);
317
if (!region->isArraylet()) {
318
j9tty_printf(PORTLIB, "validateWriteBarrier: H: dstAddress (%p) is not on an arraylet region", dstAddress);
319
printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));
320
}
321
else {
322
UDATA* arrayletParent = region->getArrayletParent(region->whichArraylet((UDATA*)dstAddress, javaVM->arrayletLeafLogSize));
323
if (arrayletParent != (UDATA*)dstObject) {
324
j9tty_printf(PORTLIB, "validateWriteBarrier: H: parent of arraylet (%p) is not destObject (%p)", arrayletParent, dstObject);
325
printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));
326
j9tty_printf(PORTLIB, "\n");
327
}
328
}
329
}
330
break;
331
}
332
default: {
333
j9tty_printf(PORTLIB, "validateWriteBarrier: unexpected arraylet type %d\n", layout);
334
assert(0);
335
}
336
};
337
break;
338
}
339
340
case GC_ObjectModel::SCAN_PRIMITIVE_ARRAY_OBJECT:
341
j9tty_printf(PORTLIB, "validateWriteBarrier: writeBarrier called on array of primitive\n");
342
j9tty_printf(PORTLIB, "value being overwritten is %d\n", GC_SlotObject::readSlot(dstAddress, compressed));
343
printClass(javaVM, J9GC_J9OBJECT_CLAZZ_VM(dstObject, javaVM));
344
j9tty_printf(PORTLIB, "\n");
345
break;
346
347
default:
348
Assert_MM_unreachable();
349
}
350
}
351
352
void
353
MM_RealtimeAccessBarrier::printClass(J9JavaVM *javaVM, J9Class* clazz)
354
{
355
J9ROMClass* romClass;
356
J9UTF8* utf;
357
PORT_ACCESS_FROM_JAVAVM(javaVM);
358
359
/* TODO: In Sov, if the class is char[], the string is printed instead of the class name */
360
romClass = clazz->romClass;
361
if(romClass->modifiers & J9AccClassArray) {
362
J9ArrayClass* arrayClass = (J9ArrayClass*) clazz;
363
UDATA arity = arrayClass->arity;
364
utf = J9ROMCLASS_CLASSNAME(arrayClass->leafComponentType->romClass);
365
j9tty_printf(PORTLIB, "%.*s", (UDATA)J9UTF8_LENGTH(utf), J9UTF8_DATA(utf));
366
while(arity--) {
367
j9tty_printf(PORTLIB, "[]");
368
}
369
} else {
370
utf = J9ROMCLASS_CLASSNAME(romClass);
371
j9tty_printf(PORTLIB, "%.*s", (UDATA)J9UTF8_LENGTH(utf), J9UTF8_DATA(utf));
372
}
373
}
374
375
/**
376
* Unmarked, heap reference, about to be deleted (or overwritten), while marking
377
* is in progress is to be remembered for later marking and scanning.
378
*/
379
void
380
MM_RealtimeAccessBarrier::rememberObject(MM_EnvironmentBase *env, J9Object *object)
381
{
382
if (_markingScheme->markObject(MM_EnvironmentRealtime::getEnvironment(env), object, true)) {
383
rememberObjectImpl(env, object);
384
}
385
}
386
387
/**
388
* Read an object from an internal VM slot (J9VMThread, J9JavaVM, named field of J9Class).
389
* This function is only concerned with moving the actual data. Do not re-implement
390
* unless the value is stored in a non-native format (e.g. compressed object pointers).
391
* See readObjectFromInternalVMSlot() for higher-level actions.
392
* In realtime, we must remember the object being read in case it's being read from an
393
* unmarked thread and stored on a marked threads' stack. If the unmarked thread terminates
394
* before being marked, we will miss the object since a stack push doesn't invoke the write
395
* barrier.
396
* @param srcAddress the address of the field to be read
397
* @param isVolatile non-zero if the field is volatile, zero otherwise
398
*/
399
mm_j9object_t
400
MM_RealtimeAccessBarrier::readObjectFromInternalVMSlotImpl(J9VMThread *vmThread, j9object_t *srcAddress, bool isVolatile)
401
{
402
mm_j9object_t object = *srcAddress;
403
if (NULL != vmThread) {
404
rememberObjectIfBarrierEnabled(vmThread, object);
405
}
406
return object;
407
}
408
409
/**
410
* Write an object to an internal VM slot (J9VMThread, J9JavaVM, named field of J9Class).
411
* In realtime, we must explicitly remember the value when being stored in case it's being
412
* read from an unmarked threads' stack and stored into a marked thread. If the unmarked
413
* thread terminates before being marked, we will miss the object since a stack pop doesn't
414
* invoke the barrier.
415
* @param destSlot the slot to be used
416
* @param value the value to be stored
417
*/
418
void
419
MM_RealtimeAccessBarrier::storeObjectToInternalVMSlot(J9VMThread *vmThread, J9Object** destSlot, J9Object *value)
420
{
421
if (preObjectStore(vmThread, destSlot, value, false)) {
422
rememberObjectIfBarrierEnabled(vmThread, value);
423
storeObjectToInternalVMSlotImpl(vmThread, destSlot, value, false);
424
postObjectStore(vmThread, destSlot, value, false);
425
}
426
}
427
428
/**
429
* Call rememberObject() if realtimeGC->isBarrierEnabled() returns true.
430
* @param object the object to remember
431
*/
432
void
433
MM_RealtimeAccessBarrier::rememberObjectIfBarrierEnabled(J9VMThread *vmThread, J9Object* object)
434
{
435
MM_EnvironmentRealtime* env = MM_EnvironmentRealtime::getEnvironment(vmThread->omrVMThread);
436
if (_realtimeGC->isBarrierEnabled()) {
437
rememberObject(env, object);
438
}
439
}
440
441
void*
442
MM_RealtimeAccessBarrier::jniGetPrimitiveArrayCritical(J9VMThread* vmThread, jarray array, jboolean *isCopy)
443
{
444
void *data = NULL;
445
J9JavaVM *javaVM = vmThread->javaVM;
446
J9InternalVMFunctions *functions = javaVM->internalVMFunctions;
447
448
J9IndexableObject *arrayObject = (J9IndexableObject*)J9_JNI_UNWRAP_REFERENCE(array);
449
bool shouldCopy = false;
450
if((javaVM->runtimeFlags & J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) == J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) {
451
shouldCopy = true;
452
} else if (!_extensions->indexableObjectModel.isInlineContiguousArraylet(arrayObject)) {
453
/* an array having discontiguous extents is another reason to force the critical section to be a copy */
454
shouldCopy = true;
455
}
456
457
if(shouldCopy) {
458
VM_VMAccess::inlineEnterVMFromJNI(vmThread);
459
GC_ArrayObjectModel* indexableObjectModel = &_extensions->indexableObjectModel;
460
I_32 sizeInElements = (I_32)indexableObjectModel->getSizeInElements(arrayObject);
461
UDATA sizeInBytes = indexableObjectModel->getDataSizeInBytes(arrayObject);
462
data = functions->jniArrayAllocateMemoryFromThread(vmThread, sizeInBytes);
463
if(NULL == data) {
464
functions->setNativeOutOfMemoryError(vmThread, 0, 0); // better error message here?
465
} else {
466
indexableObjectModel->memcpyFromArray(data, arrayObject, 0, sizeInElements);
467
if(NULL != isCopy) {
468
*isCopy = JNI_TRUE;
469
}
470
}
471
vmThread->jniCriticalCopyCount += 1;
472
VM_VMAccess::inlineExitVMToJNI(vmThread);
473
} else {
474
// acquire access and return a direct pointer
475
MM_JNICriticalRegion::enterCriticalRegion(vmThread, false);
476
data = (void *)_extensions->indexableObjectModel.getDataPointerForContiguous(arrayObject);
477
if(NULL != isCopy) {
478
*isCopy = JNI_FALSE;
479
}
480
}
481
return data;
482
}
483
484
void
485
MM_RealtimeAccessBarrier::jniReleasePrimitiveArrayCritical(J9VMThread* vmThread, jarray array, void * elems, jint mode)
486
{
487
J9JavaVM *javaVM = vmThread->javaVM;
488
J9InternalVMFunctions *functions = javaVM->internalVMFunctions;
489
490
J9IndexableObject *arrayObject = (J9IndexableObject*)J9_JNI_UNWRAP_REFERENCE(array);
491
bool shouldCopy = false;
492
if((javaVM->runtimeFlags & J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) == J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) {
493
shouldCopy = true;
494
} else if (!_extensions->indexableObjectModel.isInlineContiguousArraylet(arrayObject)) {
495
/* an array having discontiguous extents is another reason to force the critical section to be a copy */
496
shouldCopy = true;
497
}
498
499
if(shouldCopy) {
500
VM_VMAccess::inlineEnterVMFromJNI(vmThread);
501
if(JNI_ABORT != mode) {
502
GC_ArrayObjectModel* indexableObjectModel = &_extensions->indexableObjectModel;
503
I_32 sizeInElements = (I_32)indexableObjectModel->getSizeInElements(arrayObject);
504
_extensions->indexableObjectModel.memcpyToArray(arrayObject, 0, sizeInElements, elems);
505
}
506
507
// Commit means copy the data but do not free the buffer.
508
// All other modes free the buffer.
509
if(JNI_COMMIT != mode) {
510
functions->jniArrayFreeMemoryFromThread(vmThread, elems);
511
}
512
513
if(vmThread->jniCriticalCopyCount > 0) {
514
vmThread->jniCriticalCopyCount -= 1;
515
} else {
516
Assert_MM_invalidJNICall();
517
}
518
519
VM_VMAccess::inlineExitVMToJNI(vmThread);
520
} else {
521
/*
522
* Objects can not be moved if critical section is active
523
* This trace point will be generated if object has been moved or passed value of elems is corrupted
524
*/
525
void *data = (void *)_extensions->indexableObjectModel.getDataPointerForContiguous(arrayObject);
526
if(elems != data) {
527
Trc_MM_JNIReleasePrimitiveArrayCritical_invalid(vmThread, arrayObject, elems, data);
528
}
529
530
MM_JNICriticalRegion::exitCriticalRegion(vmThread, false);
531
}
532
}
533
534
const jchar*
535
MM_RealtimeAccessBarrier::jniGetStringCritical(J9VMThread* vmThread, jstring str, jboolean *isCopy)
536
{
537
jchar *data = NULL;
538
J9JavaVM *javaVM = vmThread->javaVM;
539
J9InternalVMFunctions *functions = javaVM->internalVMFunctions;
540
bool isCompressed = false;
541
bool shouldCopy = false;
542
bool hasVMAccess = false;
543
544
/* For now only copying is supported for arraylets */
545
VM_VMAccess::inlineEnterVMFromJNI(vmThread);
546
hasVMAccess = true;
547
shouldCopy = true;
548
549
if (shouldCopy) {
550
J9Object *stringObject = (J9Object*)J9_JNI_UNWRAP_REFERENCE(str);
551
J9IndexableObject *valueObject = (J9IndexableObject*)J9VMJAVALANGSTRING_VALUE(vmThread, stringObject);
552
jint length = J9VMJAVALANGSTRING_LENGTH(vmThread, stringObject);
553
UDATA sizeInBytes = length * sizeof(jchar);
554
555
if (IS_STRING_COMPRESSED(vmThread, stringObject)) {
556
isCompressed = true;
557
}
558
data = (jchar*)functions->jniArrayAllocateMemoryFromThread(vmThread, sizeInBytes);
559
if (NULL == data) {
560
functions->setNativeOutOfMemoryError(vmThread, 0, 0); // better error message here?
561
} else {
562
GC_ArrayObjectModel* indexableObjectModel = &_extensions->indexableObjectModel;
563
if (isCompressed) {
564
jint i;
565
566
for (i = 0; i < length; i++) {
567
data[i] = (jchar)(U_8)J9JAVAARRAYOFBYTE_LOAD(vmThread, (j9object_t)valueObject, i);
568
}
569
} else {
570
if (J9_ARE_ANY_BITS_SET(javaVM->runtimeFlags, J9_RUNTIME_STRING_BYTE_ARRAY)) {
571
// This API determines the stride based on the type of valueObject so in the [B case we must passin the length in bytes
572
indexableObjectModel->memcpyFromArray(data, valueObject, 0, (I_32)sizeInBytes);
573
} else {
574
indexableObjectModel->memcpyFromArray(data, valueObject, 0, length);
575
}
576
}
577
if (NULL != isCopy) {
578
*isCopy = JNI_TRUE;
579
}
580
}
581
vmThread->jniCriticalCopyCount += 1;
582
} else {
583
// acquire access and return a direct pointer
584
MM_JNICriticalRegion::enterCriticalRegion(vmThread, hasVMAccess);
585
J9Object *stringObject = (J9Object*)J9_JNI_UNWRAP_REFERENCE(str);
586
J9IndexableObject *valueObject = (J9IndexableObject*)J9VMJAVALANGSTRING_VALUE(vmThread, stringObject);
587
588
data = (jchar*)_extensions->indexableObjectModel.getDataPointerForContiguous(valueObject);
589
590
if (NULL != isCopy) {
591
*isCopy = JNI_FALSE;
592
}
593
}
594
if (hasVMAccess) {
595
VM_VMAccess::inlineExitVMToJNI(vmThread);
596
}
597
return data;
598
}
599
600
void
601
MM_RealtimeAccessBarrier::jniReleaseStringCritical(J9VMThread* vmThread, jstring str, const jchar* elems)
602
{
603
J9JavaVM *javaVM = vmThread->javaVM;
604
J9InternalVMFunctions *functions = javaVM->internalVMFunctions;
605
bool hasVMAccess = false;
606
bool shouldCopy = false;
607
608
/* For now only copying is supported for arraylets */
609
shouldCopy = true;
610
611
if (shouldCopy) {
612
// String data is not copied back
613
functions->jniArrayFreeMemoryFromThread(vmThread, (void*)elems);
614
615
if(vmThread->jniCriticalCopyCount > 0) {
616
vmThread->jniCriticalCopyCount -= 1;
617
} else {
618
Assert_MM_invalidJNICall();
619
}
620
} else {
621
// direct pointer, just drop access
622
MM_JNICriticalRegion::exitCriticalRegion(vmThread, hasVMAccess);
623
}
624
625
if (hasVMAccess) {
626
VM_VMAccess::inlineExitVMToJNI(vmThread);
627
}
628
}
629
630
#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
631
bool
632
MM_RealtimeAccessBarrier::checkClassLive(J9JavaVM *javaVM, J9Class *classPtr)
633
{
634
J9ClassLoader *classLoader = classPtr->classLoader;
635
bool result = false;
636
637
if ((0 == (classLoader->gcFlags & J9_GC_CLASS_LOADER_DEAD)) && (0 == (J9CLASS_FLAGS(classPtr) & J9AccClassDying))) {
638
/*
639
* this class has not been discovered dead yet
640
* so mark it if necessary to force it to be alive
641
*/
642
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(javaVM);
643
MM_RealtimeGC *realtimeGC = extensions->realtimeGC;
644
J9Object *classLoaderObject = classLoader->classLoaderObject;
645
646
if (NULL != classLoaderObject) {
647
if (realtimeGC->getRealtimeDelegate()->_unmarkedImpliesClasses) {
648
/*
649
* Mark is complete but GC cycle is still be in progress
650
* so we just can check is the correspondent class loader object marked
651
*/
652
result = realtimeGC->getMarkingScheme()->isMarked(classLoaderObject);
653
} else {
654
/*
655
* The return for this case is always true. If mark is active but not completed yet
656
* force this class to be marked to survive this GC
657
*/
658
659
J9VMThread* vmThread = javaVM->internalVMFunctions->currentVMThread(javaVM);
660
rememberObjectIfBarrierEnabled(vmThread, classLoaderObject);
661
result = true;
662
}
663
} else {
664
/* this class loader probably is in initialization process and class loader object has not been attached yet */
665
result = true;
666
}
667
}
668
669
return result;
670
}
671
#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */
672
673
/**
674
* Unmarked, heap reference, about to be deleted (or overwritten), while marking
675
* is in progress is to be remembered for later marking and scanning.
676
* This method is called by MM_RealtimeAccessBarrier::rememberObject()
677
*/
678
void
679
MM_RealtimeAccessBarrier::rememberObjectImpl(MM_EnvironmentBase *env, J9Object* object)
680
{
681
J9VMThread *vmThread = (J9VMThread *)env->getLanguageVMThread();
682
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(vmThread->javaVM);
683
684
extensions->sATBBarrierRememberedSet->storeInFragment(env, &vmThread->sATBBarrierRememberedSetFragment, (UDATA *)object);
685
}
686
687
void
688
MM_RealtimeAccessBarrier::forcedToFinalizableObject(J9VMThread* vmThread, J9Object* object)
689
{
690
MM_EnvironmentBase* env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);
691
if (isBarrierActive(env)) {
692
rememberObject(env, object);
693
}
694
}
695
696
/**
697
* @copydoc MM_ObjectAccessBarrier::preObjectStore()
698
*
699
* This is the implementation of the realtime write barrier.
700
*
701
* Realtime uses a snapshot-at-the-beginning algorithm, but with a fuzzy snapshot in the
702
* sense that threads are allowed to run during the root scan. This requires a "double
703
* barrier." The barrier is active from the start of root scanning through the end of
704
* tracing. For an unscanned thread performing a store, the new value is remembered by
705
* the collector. For any thread performing a store (whether scanned or not), the old
706
* value is remembered by the collector before being overwritten (thus this barrier must be
707
* positioned as a pre-store barrier). For the latter ("Yuasa barrier") aspect of the
708
* double barrier, only the first overwritten value needs to be remembered (remembering
709
* others is harmless but not needed), and so we omit synchronization on the reading of the
710
* old value.
711
*/
712
bool
713
MM_RealtimeAccessBarrier::preObjectStoreInternal(J9VMThread *vmThread, J9Object *destObject, fj9object_t *destAddress, J9Object *value, bool isVolatile)
714
{
715
MM_EnvironmentBase* env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);
716
717
if (isBarrierActive(env)) {
718
if (NULL != destObject) {
719
if (isDoubleBarrierActiveOnThread(vmThread)) {
720
rememberObject(env, value);
721
}
722
723
J9Object *oldObject = NULL;
724
protectIfVolatileBefore(vmThread, isVolatile, true, false);
725
GC_SlotObject slotObject(vmThread->javaVM->omrVM, destAddress);
726
oldObject = slotObject.readReferenceFromSlot();
727
protectIfVolatileAfter(vmThread, isVolatile, true, false);
728
rememberObject(env, oldObject);
729
}
730
}
731
732
return true;
733
}
734
735
/**
736
* @copydoc MM_ObjectAccessBarrier::preObjectStore()
737
*
738
* This is the implementation of the realtime write barrier.
739
*
740
* Realtime uses a snapshot-at-the-beginning algorithm, but with a fuzzy snapshot in the
741
* sense that threads are allowed to run during the root scan. This requires a "double
742
* barrier." The barrier is active from the start of root scanning through the end of
743
* tracing. For an unscanned thread performing a store, the new value is remembered by
744
* the collector. For any thread performing a store (whether scanned or not), the old
745
* value is remembered by the collector before being overwritten (thus this barrier must be
746
* positioned as a pre-store barrier). For the latter ("Yuasa barrier") aspect of the
747
* double barrier, only the first overwritten value needs to be remembered (remembering
748
* others is harmless but not needed), and so we omit synchronization on the reading of the
749
* old value.
750
*/
751
bool
752
MM_RealtimeAccessBarrier::preObjectStoreInternal(J9VMThread *vmThread, J9Object **destAddress, J9Object *value, bool isVolatile)
753
{
754
MM_EnvironmentBase* env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);
755
756
if (isBarrierActive(env)) {
757
if (isDoubleBarrierActiveOnThread(vmThread)) {
758
rememberObject(env, value);
759
}
760
J9Object* oldObject = NULL;
761
protectIfVolatileBefore(vmThread, isVolatile, true, false);
762
oldObject = *destAddress;
763
protectIfVolatileAfter(vmThread, isVolatile, true, false);
764
rememberObject(env, oldObject);
765
}
766
767
return true;
768
}
769
770
bool
771
MM_RealtimeAccessBarrier::preObjectStoreInternal(J9VMThread *vmThread, J9Object* destClass, J9Object **destAddress, J9Object *value, bool isVolatile)
772
{
773
/* the destClass argument is ignored, so just call the generic slot version */
774
return preObjectStoreInternal(vmThread, destAddress, value, isVolatile);
775
}
776
777
/**
778
* @copydoc MM_ObjectAccessBarrier::preObjectStore()
779
*
780
* Metronome uses a snapshot-at-the-beginning algorithm, but with a fuzzy snapshot in the
781
* sense that threads are allowed to run during the root scan. This requires a "double
782
* barrier." The barrier is active from the start of root scanning through the end of
783
* tracing. For an unscanned thread performing a store, the new value is remembered by
784
* the collector. For any thread performing a store (whether scanned or not), the old
785
* value is remembered by the collector before being overwritten (thus this barrier must be
786
* positioned as a pre-store barrier). For the latter ("Yuasa barrier") aspect of the
787
* double barrier, only the first overwritten value needs to be remembered (remembering
788
* others is harmless but not needed), and so we omit synchronization on the reading of the
789
* old value.
790
**/
791
bool
792
MM_RealtimeAccessBarrier::preObjectStore(J9VMThread *vmThread, J9Object *destObject, fj9object_t *destAddress, J9Object *value, bool isVolatile)
793
{
794
return preObjectStoreInternal(vmThread, destObject, destAddress, value, isVolatile);
795
}
796
797
/**
798
* @copydoc MM_MetronomeAccessBarrier::preObjectStore()
799
*
800
* Used for stores into classes
801
*/
802
bool
803
MM_RealtimeAccessBarrier::preObjectStore(J9VMThread *vmThread, J9Object *destClass, J9Object **destAddress, J9Object *value, bool isVolatile)
804
{
805
return preObjectStoreInternal(vmThread, destClass, destAddress, value, isVolatile);
806
}
807
808
/**
809
* @copydoc MM_MetronomeAccessBarrier::preObjectStore()
810
*
811
* Used for stores into internal structures
812
*/
813
bool
814
MM_RealtimeAccessBarrier::preObjectStore(J9VMThread *vmThread, J9Object **destAddress, J9Object *value, bool isVolatile)
815
{
816
return preObjectStoreInternal(vmThread, destAddress, value, isVolatile);
817
}
818
819
/**
820
* Enables the double barrier on the provided thread.
821
*/
822
void
823
MM_RealtimeAccessBarrier::setDoubleBarrierActiveOnThread(MM_EnvironmentBase* env)
824
{
825
MM_GCExtensions::getExtensions(env)->sATBBarrierRememberedSet->preserveLocalFragmentIndex(env, &(((J9VMThread *)env->getLanguageVMThread())->sATBBarrierRememberedSetFragment));
826
}
827
828
/**
829
* Disables the double barrier on the provided thread.
830
*/
831
void
832
MM_RealtimeAccessBarrier::setDoubleBarrierInactiveOnThread(MM_EnvironmentBase* env)
833
{
834
MM_GCExtensions::getExtensions(env)->sATBBarrierRememberedSet->restoreLocalFragmentIndex(env, &(((J9VMThread *)env->getLanguageVMThread())->sATBBarrierRememberedSetFragment));
835
}
836
837
void
838
MM_RealtimeAccessBarrier::initializeForNewThread(MM_EnvironmentBase* env)
839
{
840
MM_GCExtensions* extensions = MM_GCExtensions::getExtensions(env);
841
extensions->sATBBarrierRememberedSet->initializeFragment(env, &(((J9VMThread *)env->getLanguageVMThread())->sATBBarrierRememberedSetFragment));
842
if (isDoubleBarrierActive()) {
843
setDoubleBarrierActiveOnThread(env);
844
}
845
}
846
847
/* TODO: meter this scanning and include into utilization tracking */
848
void
849
MM_RealtimeAccessBarrier::scanContiguousArray(MM_EnvironmentRealtime *env, J9IndexableObject *objectPtr)
850
{
851
bool const compressed = env->compressObjectReferences();
852
J9JavaVM *vm = (J9JavaVM *)env->getLanguageVM();
853
#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
854
if(_realtimeGC->getRealtimeDelegate()->isDynamicClassUnloadingEnabled()) {
855
rememberObject(env, (J9Object *)objectPtr);
856
}
857
#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */
858
859
/* if NUA is enabled, separate path for contiguous arrays */
860
fj9object_t *scanPtr = (fj9object_t*) _extensions->indexableObjectModel.getDataPointerForContiguous(objectPtr);
861
fj9object_t *endScanPtr = GC_SlotObject::addToSlotAddress(scanPtr, _extensions->indexableObjectModel.getSizeInElements(objectPtr), compressed);
862
863
while(scanPtr < endScanPtr) {
864
/* since this is done from an external thread, we do not markObject, but rememberObject */
865
GC_SlotObject slotObject(vm->omrVM, scanPtr);
866
J9Object *field = slotObject.readReferenceFromSlot();
867
rememberObject(env, field);
868
scanPtr = GC_SlotObject::addToSlotAddress(scanPtr, 1, compressed);
869
}
870
/* this method assumes the array is large enough to set scan bit */
871
_markingScheme->setScanAtomic((J9Object *)objectPtr);
872
}
873
874
bool
875
MM_RealtimeAccessBarrier::markAndScanContiguousArray(MM_EnvironmentRealtime *env, J9IndexableObject *objectPtr)
876
{
877
UDATA arrayletSize = _extensions->indexableObjectModel.arrayletSize(objectPtr, /* arraylet index */ 0);
878
879
/* Sufficiently large to have a scan bit? */
880
if (arrayletSize < _extensions->minArraySizeToSetAsScanned) {
881
return false;
882
} else if (!_markingScheme->isScanned((J9Object *)objectPtr)) {
883
/* No, not scanned yet. We are going to mark it and scan right away */
884
_markingScheme->markAtomic((J9Object *)objectPtr);
885
/* The array might have been marked already (which means it will be scanned soon,
886
* or even being scanned at the moment). Regardless, we will proceed with scanning it */
887
scanContiguousArray(env, objectPtr);
888
}
889
890
return true;
891
}
892
893
/**
894
* Finds opportunities for doing the copy without executing Metronome WriteBarrier.
895
* @return ARRAY_COPY_SUCCESSFUL if copy was successful, ARRAY_COPY_NOT_DONE no copy is done
896
*/
897
I_32
898
MM_RealtimeAccessBarrier::backwardReferenceArrayCopyIndex(J9VMThread *vmThread, J9IndexableObject *srcObject, J9IndexableObject *destObject, I_32 srcIndex, I_32 destIndex, I_32 lengthInSlots)
899
{
900
MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(vmThread->omrVMThread);
901
902
/* a high level caller ensured destObject == srcObject */
903
904
if (_extensions->indexableObjectModel.isInlineContiguousArraylet(destObject)) {
905
906
if (isBarrierActive(env)) {
907
908
if (!markAndScanContiguousArray(env, destObject)) {
909
return ARRAY_COPY_NOT_DONE;
910
}
911
}
912
913
return doCopyContiguousBackward(vmThread, srcObject, destObject, srcIndex, destIndex, lengthInSlots);
914
915
}
916
917
return -2;
918
}
919
920
/**
921
* Finds opportunities for doing the copy without executing Metronome WriteBarrier.
922
* @return ARRAY_COPY_SUCCESSFUL if copy was successful, ARRAY_COPY_NOT_DONE no copy is done
923
*/
924
I_32
925
MM_RealtimeAccessBarrier::forwardReferenceArrayCopyIndex(J9VMThread *vmThread, J9IndexableObject *srcObject, J9IndexableObject *destObject, I_32 srcIndex, I_32 destIndex, I_32 lengthInSlots)
926
{
927
MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(vmThread->omrVMThread);
928
929
if (_extensions->indexableObjectModel.isInlineContiguousArraylet(destObject)
930
&& _extensions->indexableObjectModel.isInlineContiguousArraylet(srcObject)) {
931
932
if (isBarrierActive(env) ) {
933
934
if ((destObject != srcObject) && isDoubleBarrierActiveOnThread(vmThread)) {
935
return ARRAY_COPY_NOT_DONE;
936
} else {
937
if (markAndScanContiguousArray(env, destObject)) {
938
return doCopyContiguousForward(vmThread, srcObject, destObject, srcIndex, destIndex, lengthInSlots);
939
}
940
}
941
942
} else {
943
944
return doCopyContiguousForward(vmThread, srcObject, destObject, srcIndex, destIndex, lengthInSlots);
945
946
}
947
}
948
949
return -2;
950
}
951
952
#endif /* J9VM_GC_REALTIME */
953
954
955