Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ public enum JfrEvent {
GCPhasePauseLevel4Event("jdk.GCPhasePauseLevel4"),
SafepointBegin("jdk.SafepointBegin"),
SafepointEnd("jdk.SafepointEnd"),
ExecuteVMOperation("jdk.ExecuteVMOperation");
ExecuteVMOperation("jdk.ExecuteVMOperation"),
JavaMonitorEnter("jdk.JavaMonitorEnter");


private final long id;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2022, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package com.oracle.svm.core.jfr.events;

import org.graalvm.nativeimage.StackValue;

import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.jfr.JfrEvent;
import com.oracle.svm.core.jfr.JfrNativeEventWriter;
import com.oracle.svm.core.jfr.JfrNativeEventWriterData;
import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess;
import com.oracle.svm.core.jfr.JfrTicks;
import com.oracle.svm.core.jfr.SubstrateJVM;

public class JavaMonitorEnterEvent {

@Uninterruptible(reason = "Accesses a JFR buffer.")
public static void emit(Object obj, org.graalvm.nativeimage.IsolateThread previousOwner, long startTicks) {
emit(obj, com.oracle.svm.core.jfr.SubstrateJVM.get().getThreadId(previousOwner), startTicks);
}

@Uninterruptible(reason = "Accesses a JFR buffer.")
public static void emit(Object obj, long previousOwner, long startTicks) {
if (SubstrateJVM.isRecording() && SubstrateJVM.get().isEnabled(JfrEvent.JavaMonitorEnter)) {
JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class);
JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data);

JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.JavaMonitorEnter);
JfrNativeEventWriter.putLong(data, startTicks);
JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks() - startTicks);
JfrNativeEventWriter.putEventThread(data);
JfrNativeEventWriter.putLong(data, SubstrateJVM.get().getStackTraceId(JfrEvent.JavaMonitorEnter.getId(), 0));
JfrNativeEventWriter.putClass(data, obj.getClass());
JfrNativeEventWriter.putLong(data, previousOwner);
JfrNativeEventWriter.putLong(data, org.graalvm.compiler.word.Word.objectToUntrackedPointer(obj).rawValue());
JfrNativeEventWriter.endSmallEvent(data);

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2022, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package com.oracle.svm.core.monitor;

import java.util.concurrent.locks.ReentrantLock;

import org.graalvm.nativeimage.CurrentIsolate;
import com.oracle.svm.core.jfr.SubstrateJVM;
import com.oracle.svm.core.jfr.events.JavaMonitorEnterEvent;
import com.oracle.svm.core.jfr.JfrTicks;

public class JavaMonitor extends ReentrantLock {
private long ownerTid;

public long getOwnerTid() {
return ownerTid;
}

public JavaMonitor() {
super();
ownerTid = SubstrateJVM.get().getThreadId(CurrentIsolate.getCurrentThread());
}

public void monitorEnter(Object obj) {
if (!tryLock()) {
long startTicks = JfrTicks.elapsedTicks();
lock();
JavaMonitorEnterEvent.emit(obj, getOwnerTid(), startTicks);
}
ownerTid = SubstrateJVM.get().getThreadId(CurrentIsolate.getCurrentThread());
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import com.oracle.svm.core.thread.VMOperationControl;
import com.oracle.svm.core.util.VMError;


import jdk.internal.misc.Unsafe;

/**
Expand Down Expand Up @@ -137,7 +138,7 @@ public class MultiThreadedMonitorSupport extends MonitorSupport {
* java.io.FileInputStream.close() which synchronizes on a 'Object closeLock = new
* Object()' object. We cannot modify the type of the monitor since it is in JDK code.
* Adding a monitor slot to java.lang.Object doesn't impact any subtypes.
*
*
* This should also take care of the synchronization in
* ReferenceInternals.processPendingReferences().
*/
Expand Down Expand Up @@ -192,7 +193,7 @@ public class MultiThreadedMonitorSupport extends MonitorSupport {
* Secondary storage for monitor slots. Synchronized to prevent concurrent access and
* modification.
*/
private final Map<Object, ReentrantLock> additionalMonitors = new WeakIdentityHashMap<>();
private final Map<Object, JavaMonitor> additionalMonitors = new WeakIdentityHashMap<>();
private final ReentrantLock additionalMonitorsLock = new ReentrantLock();

@Override
Expand Down Expand Up @@ -255,8 +256,8 @@ private static void slowPathMonitorEnter(Object obj) {
@RestrictHeapAccess(reason = NO_LONGER_UNINTERRUPTIBLE, access = Access.UNRESTRICTED)
@Override
public void monitorEnter(Object obj) {
ReentrantLock lockObject = getOrCreateMonitor(obj, true);
lockObject.lock();
JavaMonitor lockObject = getOrCreateMonitor(obj, true);
lockObject.monitorEnter(obj);
}

@SubstrateForeignCallTarget(stubCallingConvention = false)
Expand Down Expand Up @@ -301,13 +302,13 @@ public Object prepareRelockObject(Object obj) {
/*
* We ensure that the lock for the object exists, so that the actual re-locking during
* deoptimization can be uninterruptible.
*
*
* Unfortunately, we cannot do any assertion checking in this method: deoptimization can run
* in any thread, i.e., not necessarily in the thread that the lock will be for. And while
* the frame that is deoptimized must have had the object locked, the thread could have
* given up the lock as part of a wait() - so at this time any thread is allowed to hold the
* lock.
*
*
* Because any thread can hold the lock at this time, there is no way we can patch any
* internal state of the lock immediately here. The actual state patching therefore happens
* later in doRelockObject.
Expand Down Expand Up @@ -421,7 +422,7 @@ protected static Object replaceObject(Object unreplacedObject) {
return unreplacedObject;
}

protected final ReentrantLock getOrCreateMonitor(Object unreplacedObject, boolean createIfNotExisting) {
protected final JavaMonitor getOrCreateMonitor(Object unreplacedObject, boolean createIfNotExisting) {
Object obj = replaceObject(unreplacedObject);
assert obj != null;
int monitorOffset = getMonitorOffset(obj);
Expand All @@ -434,48 +435,48 @@ protected final ReentrantLock getOrCreateMonitor(Object unreplacedObject, boolea
}
}

protected ReentrantLock getOrCreateMonitorFromObject(Object obj, boolean createIfNotExisting, int monitorOffset) {
ReentrantLock existingMonitor = (ReentrantLock) BarrieredAccess.readObject(obj, monitorOffset);
protected JavaMonitor getOrCreateMonitorFromObject(Object obj, boolean createIfNotExisting, int monitorOffset) {
JavaMonitor existingMonitor = (JavaMonitor) BarrieredAccess.readObject(obj, monitorOffset);
if (existingMonitor != null || !createIfNotExisting) {
assert existingMonitor == null || isMonitorLock(existingMonitor);
return existingMonitor;
}
/* Atomically put a new lock in place of the null at the monitorOffset. */
ReentrantLock newMonitor = newMonitorLock();
JavaMonitor newMonitor = newMonitorLock();
if (UNSAFE.compareAndSetObject(obj, monitorOffset, null, newMonitor)) {
return newMonitor;
}
/* We lost the race, use the lock some other thread installed. */
return (ReentrantLock) BarrieredAccess.readObject(obj, monitorOffset);
return (JavaMonitor) BarrieredAccess.readObject(obj, monitorOffset);
}

protected ReentrantLock getOrCreateMonitorFromMap(Object obj, boolean createIfNotExisting) {
protected JavaMonitor getOrCreateMonitorFromMap(Object obj, boolean createIfNotExisting) {
assert obj.getClass() != Target_java_lang_ref_ReferenceQueue_Lock.class : "ReferenceQueue.Lock must have a monitor field or we can deadlock accessing WeakIdentityHashMap below";
VMError.guarantee(!additionalMonitorsLock.isHeldByCurrentThread(),
"Recursive manipulation of the additionalMonitors map can lead to table corruptions and double insertion of a monitor for the same object");
"Recursive manipulation of the additionalMonitors map can lead to table corruptions and double insertion of a monitor for the same object");

/*
* Lock the monitor map and maybe add a monitor for this object. This serialization might be
* a scalability problem.
*/
additionalMonitorsLock.lock();
try {
ReentrantLock existingMonitor = additionalMonitors.get(obj);
JavaMonitor existingMonitor = additionalMonitors.get(obj);
if (existingMonitor != null || !createIfNotExisting) {
assert existingMonitor == null || isMonitorLock(existingMonitor);
return existingMonitor;
}
ReentrantLock newMonitor = newMonitorLock();
ReentrantLock previousEntry = additionalMonitors.put(obj, newMonitor);
JavaMonitor newMonitor = newMonitorLock();
JavaMonitor previousEntry = additionalMonitors.put(obj, newMonitor);
VMError.guarantee(previousEntry == null, "Replaced monitor in secondary storage map");
return newMonitor;
} finally {
additionalMonitorsLock.unlock();
}
}

protected static ReentrantLock newMonitorLock() {
ReentrantLock newMonitor = new ReentrantLock();
protected static JavaMonitor newMonitorLock() {
JavaMonitor newMonitor = new JavaMonitor();
Target_java_util_concurrent_locks_ReentrantLock lock = SubstrateUtil.cast(newMonitor, Target_java_util_concurrent_locks_ReentrantLock.class);
Target_java_util_concurrent_locks_ReentrantLock_NonfairSync sync = SubstrateUtil.cast(lock.sync, Target_java_util_concurrent_locks_ReentrantLock_NonfairSync.class);
sync.objectMonitorCondition = SubstrateUtil.cast(MONITOR_WITHOUT_CONDITION, Target_java_util_concurrent_locks_AbstractQueuedSynchronizer_ConditionObject.class);
Expand Down