1 /*
 2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
 3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 4  *
 5  * This code is free software; you can redistribute it and/or modify it
 6  * under the terms of the GNU General Public License version 2 only, as
 7  * published by the Free Software Foundation.
 8  *
 9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @summary Test ThreadMXBean.getLockedMonitors returns information about an object
27  *    monitor lock entered with a synchronized native method or JNI MonitorEnter
28  * @run junit/othervm LockedMonitorInNative
29  */
30 
31 import java.lang.management.ManagementFactory;
32 import java.lang.management.ThreadInfo;
33 import java.lang.management.ThreadMXBean;
34 import java.util.Arrays;
35 
36 import org.junit.jupiter.api.Test;
37 import org.junit.jupiter.api.BeforeAll;
38 import static org.junit.jupiter.api.Assertions.*;
39 
40 public class LockedMonitorInNative {
41 
42     @BeforeAll
43     static void setup() throws Exception {
44         System.loadLibrary("LockedMonitorInNative");
45     }
46 
47     /**
48      * Test ThreadMXBean.getLockedMonitors returns information about an object
49      * monitor lock entered with a synchronized native method.
50      */
51     @Test
52     void testSynchronizedNative() {
53         Object lock = this;
54         runWithSynchronizedNative(() -> {
55             assertTrue(holdsLock(lock), "Thread does not hold lock");
56         });
57     }
58 
59     /**
60      * Test ThreadMXBean.getLockedMonitors returns information about an object
61      * monitor lock entered with JNI MonitorEnter.
62      */
63     @Test
64     void testMonitorEnteredInNative() {
65         var lock = new Object();
66         runWithMonitorEnteredInNative(lock, () -> {
67             assertTrue(holdsLock(lock), "Thread does not hold lock");
68         });
69     }
70 
71     private boolean holdsLock(Object lock) {
72         int hc = System.identityHashCode(lock);
73         long tid = Thread.currentThread().threadId();
74         ThreadInfo ti = ManagementFactory.getPlatformMXBean(ThreadMXBean.class)
75                 .getThreadInfo(new long[] { tid }, true, true)[0];
76         return Arrays.stream(ti.getLockedMonitors())
77                 .anyMatch(mi -> mi.getIdentityHashCode() == hc);
78     }
79 
80     /**
81      * Invokes the given task's run method while holding the monitor for "this".
82      */
83     private synchronized native void runWithSynchronizedNative(Runnable task);
84 
85     /**
86      * Invokes the given task's run method while holding the monitor for the given
87      * object. The monitor is entered with JNI MonitorEnter, and exited with JNI MonitorExit.
88      */
89     private native void runWithMonitorEnteredInNative(Object lock, Runnable task);
90 
91     /**
92      * Called from native methods to run the given task.
93      */
94     private void run(Runnable task) {
95         task.run();
96     }
97 }