Path: blob/master/runtime/gc_glue_java/JNICriticalRegion.hpp
5985 views
/*******************************************************************************1* Copyright (c) 1991, 2019 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*******************************************************************************/212223/**24* @file25* @ingroup GC_Base26*/2728#if !defined(JNICRITICALREGION_HPP_)29#define JNICRITICALREGION_HPP_3031#include "modron.h"32#include "ModronAssertions.h"3334#include "AtomicSupport.hpp"35#include "BaseNonVirtual.hpp"36#include "VMAccess.hpp"3738class MM_JNICriticalRegion : public MM_BaseNonVirtual39{40/* data members */41private:42protected:43public:4445/* member function */46private:47protected:48public:49static void reacquireAccess(J9VMThread* vmThread, UDATA accessMask);50static void releaseAccess(J9VMThread* vmThread, UDATA* accessMask);5152/**53* Enter a JNI critical region (i.e. GetPrimitiveArrayCritical or GetStringCritical).54* Once a thread has successfully entered a critical region, it has privileges similar55* to holding VM access. No object can move while any thread is in a critical region.56*57* @param vmThread the J9VMThread requesting to enter a critical region58* @param hasVMAccess true if caller has acquired VM access, false if not59*/60static MMINLINE void61enterCriticalRegion(J9VMThread* vmThread, bool hasVMAccess)62{63if (J9_ARE_ANY_BITS_SET(vmThread->publicFlags, J9_PUBLIC_FLAGS_DEBUG_VM_ACCESS)) {64Assert_MM_true(J9_VM_FUNCTION(vmThread, currentVMThread)(vmThread->javaVM) == vmThread);65}6667/* Handle nested case first to avoid the unnecessary atomic */68if (J9_ARE_ANY_BITS_SET(vmThread->publicFlags, J9_PUBLIC_FLAGS_JNI_CRITICAL_REGION)) {69/* Nested critical region; increment the count */70vmThread->jniCriticalDirectCount += 1;71} else {72UDATA const criticalFlags = J9_PUBLIC_FLAGS_JNI_CRITICAL_REGION | J9_PUBLIC_FLAGS_JNI_CRITICAL_ACCESS;73#if defined(J9VM_INTERP_ATOMIC_FREE_JNI)74UDATA const expectedFlags = J9_PUBLIC_FLAGS_VM_ACCESS;75#else /* J9VM_INTERP_ATOMIC_FREE_JNI */76UDATA const expectedFlags = hasVMAccess ? J9_PUBLIC_FLAGS_VM_ACCESS : 0;77#endif /* J9VM_INTERP_ATOMIC_FREE_JNI */78/* Expected case: swap in JNI access bits */79if (expectedFlags == VM_AtomicSupport::lockCompareExchange(&vmThread->publicFlags, expectedFlags, expectedFlags | criticalFlags)) {80/* First entry into a critical region */81vmThread->jniCriticalDirectCount = 1;82} else {83omrthread_t const osThread = vmThread->osThread;84omrthread_monitor_t const publicFlagsMutex = vmThread->publicFlagsMutex;85omrthread_monitor_enter_using_threadId(publicFlagsMutex, osThread);86if (hasVMAccess) {87/* Entering the first critical region with VM access; set the critical flags */88VM_VMAccess::setPublicFlags(vmThread, criticalFlags);89vmThread->jniCriticalDirectCount = 1;9091/* The current thread has VM access and just acquired JNI critical access.92* If an exclusive request is in progress and the current thread has already93* been requested to halt, then adjust the JNI response count accordingly.94*/95if (J9_ARE_ANY_BITS_SET(vmThread->publicFlags, J9_PUBLIC_FLAGS_HALT_THREAD_EXCLUSIVE)) {96J9JavaVM* const vm = vmThread->javaVM;97omrthread_monitor_t const exclusiveAccessMutex = vm->exclusiveAccessMutex;98omrthread_monitor_enter_using_threadId(exclusiveAccessMutex, osThread);99vm->jniCriticalResponseCount += 1;100omrthread_monitor_exit_using_threadId(exclusiveAccessMutex, osThread);101}102} else {103/* Entering the first critical region; acquire VM access and set the critical flags */104if (0 == VM_AtomicSupport::lockCompareExchange(&vmThread->publicFlags, 0, criticalFlags)) {105/* Set the count to 1 */106vmThread->jniCriticalDirectCount = 1;107} else {108J9_VM_FUNCTION(vmThread, internalEnterVMFromJNI)(vmThread);109VM_VMAccess::setPublicFlags(vmThread, J9_PUBLIC_FLAGS_JNI_CRITICAL_REGION | J9_PUBLIC_FLAGS_JNI_CRITICAL_ACCESS);110vmThread->jniCriticalDirectCount = 1;111J9_VM_FUNCTION(vmThread, internalExitVMToJNI)(vmThread);112}113}114omrthread_monitor_exit_using_threadId(publicFlagsMutex, osThread);115}116}117}118119/**120* Exit a JNI critical region (i.e. ReleasePrimitiveArrayCritical or ReleaseStringCritical).121* Once a thread has successfully exited a critical region, objects in the java122* heap are allowed to move again.123*124* @param vmThread the J9VMThread requesting to exit a critical region125* @param hasVMAccess true if caller has acquired VM access, false if not126*/127static MMINLINE void128exitCriticalRegion(J9VMThread* vmThread, bool hasVMAccess)129{130if (J9_ARE_ANY_BITS_SET(vmThread->publicFlags, J9_PUBLIC_FLAGS_DEBUG_VM_ACCESS)) {131Assert_MM_true(J9_VM_FUNCTION(vmThread, currentVMThread)(vmThread->javaVM) == vmThread);132}133134Assert_MM_mustHaveJNICriticalRegion(vmThread);135if (--vmThread->jniCriticalDirectCount == 0) {136/* Exiting last critical region, swap out critical flags */137UDATA const criticalFlags = J9_PUBLIC_FLAGS_JNI_CRITICAL_REGION | J9_PUBLIC_FLAGS_JNI_CRITICAL_ACCESS;138#if defined(J9VM_INTERP_ATOMIC_FREE_JNI)139UDATA const finalFlags = J9_PUBLIC_FLAGS_VM_ACCESS;140#else /* J9VM_INTERP_ATOMIC_FREE_JNI */141UDATA const finalFlags = hasVMAccess ? J9_PUBLIC_FLAGS_VM_ACCESS : 0;142#endif /* J9VM_INTERP_ATOMIC_FREE_JNI */143UDATA const jniAccess = criticalFlags | finalFlags;144if (jniAccess != VM_AtomicSupport::lockCompareExchange(&vmThread->publicFlags, jniAccess, finalFlags)) {145/* Exiting the last critical region; clear the critical flags.146* Cache a copy of the flags first to determine if we must respond to an exclusive access request.147*/148omrthread_t const osThread = vmThread->osThread;149omrthread_monitor_t const publicFlagsMutex = vmThread->publicFlagsMutex;150omrthread_monitor_enter_using_threadId(publicFlagsMutex, osThread);151UDATA const publicFlags = VM_VMAccess::clearPublicFlagsNoMutex(vmThread, criticalFlags);152if (J9_ARE_ALL_BITS_SET(publicFlags, J9_PUBLIC_FLAGS_JNI_CRITICAL_ACCESS | J9_PUBLIC_FLAGS_HALT_THREAD_EXCLUSIVE)) {153/* If an exclusive request is pending, then respond. */154J9JavaVM* const vm = vmThread->javaVM;155omrthread_monitor_t const exclusiveAccessMutex = vm->exclusiveAccessMutex;156omrthread_monitor_enter_using_threadId(exclusiveAccessMutex, osThread);157PORT_ACCESS_FROM_JAVAVM(vm);158U_64 const timeNow = VM_VMAccess::updateExclusiveVMAccessStats(vmThread, vm, PORTLIB);159if (--vm->jniCriticalResponseCount == 0) {160VM_VMAccess::respondToExclusiveRequest(vmThread, vm, PORTLIB, timeNow, J9_EXCLUSIVE_SLOW_REASON_JNICRITICAL);161}162omrthread_monitor_exit_using_threadId(exclusiveAccessMutex, osThread);163}164omrthread_monitor_exit_using_threadId(publicFlagsMutex, osThread);165}166}167}168};169170#endif /* JNICRITICALREGION_HPP_ */171172173