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  * @bug 8247972
 27  *
 28  * @summary converted from VM Testbase nsk/jvmti/GetObjectMonitorUsage/objmonusage003
 29  * DESCRIPTION
 30  *     The test checks if the JVMTI function GetObjectMonitorUsage returns
 31  *     the expected values for the owner, entry_count, waiter_count
 32  *     fields of JVMTI_monitor_info.
 33  *     The testcases are the following:
 34  *       - unowned object without any threads waiting
 35  *       - unowned object with threads waiting to be notified
 36  *       - owned object without any threads waiting
 37  *       - owned object with N threads waiting to enter the monitor
 38  *       - owned object with N threads waiting to be notified
 39  *       - owned object with N threads waiting to enter, from 0 to N threads
 40  *         waiting to re-enter, from N to 0 threads waiting to be notified
 41  *       - all the above scenarios are executed with platform and virtual threads
 42  * @requires vm.jvmti
 43  * @run main/othervm/native
 44  *     -Djdk.virtualThreadScheduler.parallelism=10
 45  *     -agentlib:ObjectMonitorUsage ObjectMonitorUsage
 46  */
 47 
 48 public class ObjectMonitorUsage {
 49     final static int NUMBER_OF_ENTERING_THREADS = 4;
 50     final static int NUMBER_OF_WAITING_THREADS  = 4;
 51     final static int NUMBER_OF_THREADS = NUMBER_OF_ENTERING_THREADS + NUMBER_OF_WAITING_THREADS;
 52 
 53     static Object lockCheck = new Object();
 54 
 55     native static int getRes();
 56     native static int setTestedMonitor(Object monitor);
 57     native static void ensureBlockedOnEnter(Thread thread);
 58     native static void ensureWaitingToBeNotified(Thread thread);
 59     native static void check(Object obj, Thread owner,
 60                              int entryCount, int waiterCount, int notifyWaiterCount);
 61 
 62     static void log(String msg) {
 63         System.out.println(msg);
 64     }
 65 
 66     static String vtag(boolean isVirtual) {
 67         return isVirtual ? "virtual" : "platform";
 68     }
 69 
 70     static void sleep(long millis) {
 71         try {
 72             Thread.sleep(millis);
 73         } catch (InterruptedException e) {
 74             // ignore
 75         }
 76     }
 77 
 78     static Thread startTask(int idx, TestTask task, boolean isVirtual, String kind) {
 79         Thread thread = isVirtual ? Thread.ofVirtual().name(kind + "VT" + idx).start(task)
 80                                   : Thread.ofPlatform().name(kind + "PT" + idx).start(task);
 81         task.setName(thread.getName());
 82         task.waitReady();
 83         return thread;
 84     }
 85 
 86     static Thread[] startWaitingThreads(boolean isVirtual) {
 87         Thread[] threads = new Thread[NUMBER_OF_WAITING_THREADS];
 88         for (int i = 0; i < NUMBER_OF_WAITING_THREADS; i++) {
 89             // the WaitingTask has to wait to be notified in lockCheck.wait()
 90             Thread thread = startTask(i, new WaitingTask(), isVirtual, "Waiting");
 91             ensureWaitingToBeNotified(thread);
 92             threads[i] = thread;
 93         }
 94         return threads;
 95     }
 96 
 97     static Thread[] startEnteringThreads(boolean isVirtual) {
 98         Thread[] threads = new Thread[NUMBER_OF_ENTERING_THREADS];
 99         for (int i = 0; i < NUMBER_OF_ENTERING_THREADS; i++) {
100             // the EnteringTask has to be blocked at the lockCheck enter
101             Thread thread = startTask(i, new EnteringTask(), isVirtual, "Entering");
102             ensureBlockedOnEnter(thread);
103             threads[i] = thread;
104         }
105         return threads;
106     }
107 
108     static void joinThreads(Thread[] threads) {
109         try {
110             for (Thread t : threads) {
111                 t.join();
112             }
113         } catch (InterruptedException e) {
114             throw new Error("Unexpected " + e);
115         }
116     }
117 
118     /* Scenario #0:
119      * - owning:         0
120      * - entering:       0
121      * - re-entering:    0
122      * - to be notified: N
123      */
124     static void test0(boolean isVirtual) {
125         String vtag = vtag(isVirtual);
126         log("\n### test0: started " + vtag);
127 
128         setTestedMonitor(lockCheck);
129         Thread[] wThreads = startWaitingThreads(isVirtual);
130 
131         // entry count: 0
132         // count of threads waiting to enter:       0
133         // count of threads waiting to re-enter:    0
134         // count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS
135         check(lockCheck, null, 0, // no owner thread
136               0, // count of threads waiting to enter: 0
137               NUMBER_OF_WAITING_THREADS);
138 
139         synchronized (lockCheck) {
140             lockCheck.notifyAll();
141         }
142         joinThreads(wThreads);
143         setTestedMonitor(null);
144         log("### test0: finished " + vtag);
145     }
146 
147     /* Scenario #1:
148      * - owning:         1
149      * - entering:       N
150      * - re-entering:    0
151      * - to be notified: 0
152      */
153     static void test1(boolean isVirtual) {
154         String vtag = vtag(isVirtual);
155         log("\n### test1: started " + vtag);
156 
157         setTestedMonitor(lockCheck);
158         Thread[] eThreads = null;
159 
160         synchronized (lockCheck) {
161             // entry count: 1
162             // count of threads waiting to enter: 0
163             // count of threads waiting to re-enter: 0
164             // count of threads waiting to be notified: 0
165             check(lockCheck, Thread.currentThread(), 1, 0, 0);
166 
167             eThreads = startEnteringThreads(isVirtual);
168 
169             // entry count: 1
170             // count of threads waiting to enter:       NUMBER_OF_ENTERING_THREADS
171             // count of threads waiting to re-enter:    0
172             // count of threads waiting to be notified: 0
173             check(lockCheck, Thread.currentThread(), 1,
174                   NUMBER_OF_ENTERING_THREADS,
175                   0 /* count of threads waiting to be notified: 0 */);
176 
177         }
178         joinThreads(eThreads);
179         setTestedMonitor(null);
180         log("### test1: finished " + vtag);
181     }
182 
183     /* Scenario #2:
184      * - owning:         1
185      * - entering:       N
186      * - re-entering:    0
187      * - to be notified: N
188      */
189     static void test2(boolean isVirtual) throws Error {
190         String vtag = vtag(isVirtual);
191         log("\n### test2: started " + vtag);
192 
193         setTestedMonitor(lockCheck);
194         Thread[] wThreads = startWaitingThreads(isVirtual);
195         Thread[] eThreads = null;
196 
197         synchronized (lockCheck) {
198             eThreads = startEnteringThreads(isVirtual);
199 
200             // entry count: 1
201             // count of threads waiting to enter:       NUMBER_OF_ENTERING_THREADS
202             // count of threads waiting to re-enter:    0
203             // count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS
204             check(lockCheck, Thread.currentThread(), 1,
205                   NUMBER_OF_ENTERING_THREADS,
206                   NUMBER_OF_WAITING_THREADS);
207 
208             lockCheck.notifyAll();
209         }
210         joinThreads(wThreads);
211         joinThreads(eThreads);
212         setTestedMonitor(null);
213         log("### test2: finished " + vtag);
214     }
215 
216     /* Scenario #3:
217      * Initially we have:
218      * - owning:         1
219      * - entering:       0
220      * - re-entering:    0
221      * - to be notified: N
222      *
223      * The threads waiting to be notified are being notified one-by-one
224      * until all threads are blocked on re-entering the monitor.
225      * The numbers of entering/re-entering and waiting threads are checked
226      * for correctness after each notification.
227      */
228     static void test3(boolean isVirtual) throws Error {
229         String vtag = vtag(isVirtual);
230         log("\n### test3: started " + vtag);
231 
232         setTestedMonitor(lockCheck);
233         Thread[] wThreads = startWaitingThreads(isVirtual);
234         Thread[] eThreads = null;
235 
236         synchronized (lockCheck) {
237             // entry count: 1
238             // count of threads waiting to enter:       0
239             // count of threads waiting to re-enter:    0
240             // count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS
241             check(lockCheck, Thread.currentThread(), 1,
242                   0, // number of threads waiting to enter or re-enter
243                   NUMBER_OF_WAITING_THREADS);
244 
245             eThreads = startEnteringThreads(isVirtual);
246 
247             // entry count: 1
248             // count of threads waiting to enter:       NUMBER_OF_ENTERING_THREADS
249             // count of threads waiting to re-enter:    0
250             // count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS
251             check(lockCheck, Thread.currentThread(), 1,
252                   NUMBER_OF_ENTERING_THREADS,
253                   NUMBER_OF_WAITING_THREADS);
254 
255             for (int i = 0; i < NUMBER_OF_WAITING_THREADS; i++) {
256                 lockCheck.notify(); // notify waiting threads one by one
257                 // now the notified WaitingTask has to be blocked on the lockCheck re-enter
258 
259                 // entry count: 1
260                 // count of threads waiting to enter:       NUMBER_OF_ENTERING_THREADS
261                 // count of threads waiting to re-enter:    i + 1
262                 // count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS - i - 1
263                 check(lockCheck, Thread.currentThread(), 1,
264                       NUMBER_OF_ENTERING_THREADS + i + 1,
265                       NUMBER_OF_WAITING_THREADS  - i - 1);
266             }
267         }
268         joinThreads(wThreads);
269         joinThreads(eThreads);
270         setTestedMonitor(null);
271         log("### test3: finished " + vtag);
272     }
273 
274     static void test(boolean isVirtual) {
275         test0(isVirtual);
276         test1(isVirtual);
277         test2(isVirtual);
278         test3(isVirtual);
279     }
280 
281     public static void main(String args[]) {
282         log("\n### main: started\n");
283         check(lockCheck, null, 0, 0, 0);
284 
285         test(false); // test platform threads
286         test(true);  // test virtual threads
287 
288         check(lockCheck, null, 0, 0, 0);
289         if (getRes() > 0) {
290             throw new RuntimeException("Failed status returned from the agent");
291         }
292         log("\n### main: finished\n");
293     }
294 
295     static abstract class TestTask implements Runnable {
296         volatile boolean ready = false;
297         String name;
298 
299         public abstract void run();
300 
301         String getName() { return name; }
302         void setName(String name) { this.name = name; }
303 
304         void waitReady() {
305             try {
306                 while (!ready) {
307                     Thread.sleep(10);
308                 }
309             } catch (InterruptedException e) {
310                 throw new Error("Unexpected " + e);
311             }
312         }
313     }
314 
315     static class EnteringTask extends TestTask {
316         public void run() {
317             ready = true;
318             synchronized (lockCheck) {
319             }
320         }
321     }
322 
323     static class WaitingTask extends TestTask {
324         public void run() {
325             synchronized (lockCheck) {
326                 try {
327                     ready = true;
328                     // no protection against spurious wakeups here
329                     lockCheck.wait();
330                 } catch (InterruptedException e) {
331                     throw new Error("Unexpected " + e);
332                 }
333             }
334         }
335     }
336 }