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 waitsToEnter();
 57     native static int waitsToBeNotified();
 58     native static int setTestedMonitor(Object monitor);
 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             threads[i] = startTask(i, new WaitingTask(), isVirtual, "Waiting");
 91         }
 92         while (waitsToBeNotified() < NUMBER_OF_WAITING_THREADS) {
 93             sleep(1);
 94         }
 95         return threads;
 96     }
 97 
 98     static Thread[] startEnteringThreads(boolean isVirtual) {
 99         Thread[] threads = new Thread[NUMBER_OF_ENTERING_THREADS];
100         for (int i = 0; i < NUMBER_OF_ENTERING_THREADS; i++) {
101             // the EnteringTask has to be blocked at the lockCheck enter
102             threads[i] = startTask(i, new EnteringTask(), isVirtual, "Entering");
103         }
104         while (waitsToEnter() < NUMBER_OF_ENTERING_THREADS) {
105             sleep(1);
106         }
107         return threads;
108     }
109 
110     static void joinThreads(Thread[] threads) {
111         try {
112             for (Thread t : threads) {
113                 t.join();
114             }
115         } catch (InterruptedException e) {
116             throw new Error("Unexpected " + e);
117         }
118     }
119 
120     /* Scenario #0:
121      * - owning:         0
122      * - entering:       0
123      * - re-entering:    0
124      * - to be notified: N
125      */
126     static void test0(boolean isVirtual) {
127         String vtag = vtag(isVirtual);
128         log("\n### test0: started " + vtag);
129 
130         setTestedMonitor(lockCheck);
131         Thread[] wThreads = startWaitingThreads(isVirtual);
132 
133         // entry count: 0
134         // count of threads waiting to enter:       0
135         // count of threads waiting to re-enter:    0
136         // count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS
137         check(lockCheck, null, 0, // no owner thread
138               0, // count of threads waiting to enter: 0
139               NUMBER_OF_WAITING_THREADS);
140 
141         synchronized (lockCheck) {
142             lockCheck.notifyAll();
143         }
144         joinThreads(wThreads);
145         setTestedMonitor(null);
146         log("### test0: finished " + vtag);
147     }
148 
149     /* Scenario #1:
150      * - owning:         1
151      * - entering:       N
152      * - re-entering:    0
153      * - to be notified: 0
154      */
155     static void test1(boolean isVirtual) {
156         String vtag = vtag(isVirtual);
157         log("\n### test1: started " + vtag);
158 
159         setTestedMonitor(lockCheck);
160         Thread[] eThreads = null;
161 
162         synchronized (lockCheck) {
163             // entry count: 1
164             // count of threads waiting to enter: 0
165             // count of threads waiting to re-enter: 0
166             // count of threads waiting to be notified: 0
167             check(lockCheck, Thread.currentThread(), 1, 0, 0);
168 
169             eThreads = startEnteringThreads(isVirtual);
170 
171             // entry count: 1
172             // count of threads waiting to enter:       NUMBER_OF_ENTERING_THREADS
173             // count of threads waiting to re-enter:    0
174             // count of threads waiting to be notified: 0
175             check(lockCheck, Thread.currentThread(), 1,
176                   NUMBER_OF_ENTERING_THREADS,
177                   0 /* count of threads waiting to be notified: 0 */);
178 
179         }
180         joinThreads(eThreads);
181         setTestedMonitor(null);
182         log("### test1: finished " + vtag);
183     }
184 
185     /* Scenario #2:
186      * - owning:         1
187      * - entering:       N
188      * - re-entering:    0
189      * - to be notified: N
190      */
191     static void test2(boolean isVirtual) throws Error {
192         String vtag = vtag(isVirtual);
193         log("\n### test2: started " + vtag);
194 
195         setTestedMonitor(lockCheck);
196         Thread[] wThreads = startWaitingThreads(isVirtual);
197         Thread[] eThreads = null;
198 
199         synchronized (lockCheck) {
200             eThreads = startEnteringThreads(isVirtual);
201 
202             // entry count: 1
203             // count of threads waiting to enter:       NUMBER_OF_ENTERING_THREADS
204             // count of threads waiting to re-enter:    0
205             // count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS
206             check(lockCheck, Thread.currentThread(), 1,
207                   NUMBER_OF_ENTERING_THREADS,
208                   NUMBER_OF_WAITING_THREADS);
209 
210             lockCheck.notifyAll();
211         }
212         joinThreads(wThreads);
213         joinThreads(eThreads);
214         setTestedMonitor(null);
215         log("### test2: finished " + vtag);
216     }
217 
218     /* Scenario #3:
219      * Initially we have:
220      * - owning:         1
221      * - entering:       0
222      * - re-entering:    0
223      * - to be notified: N
224      *
225      * The threads waiting to be notified are being notified one-by-one
226      * until all threads are blocked on re-entering the monitor.
227      * The numbers of entering/re-entering and waiting threads are checked
228      * for correctness after each notification.
229      */
230     static void test3(boolean isVirtual) throws Error {
231         String vtag = vtag(isVirtual);
232         log("\n### test3: started " + vtag);
233 
234         setTestedMonitor(lockCheck);
235         Thread[] wThreads = startWaitingThreads(isVirtual);
236         Thread[] eThreads = null;
237 
238         synchronized (lockCheck) {
239             // entry count: 1
240             // count of threads waiting to enter:       0
241             // count of threads waiting to re-enter:    0
242             // count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS
243             check(lockCheck, Thread.currentThread(), 1,
244                   0, // number of threads waiting to enter or re-enter
245                   NUMBER_OF_WAITING_THREADS);
246 
247             eThreads = startEnteringThreads(isVirtual);
248 
249             // entry count: 1
250             // count of threads waiting to enter:       NUMBER_OF_ENTERING_THREADS
251             // count of threads waiting to re-enter:    0
252             // count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS
253             check(lockCheck, Thread.currentThread(), 1,
254                   NUMBER_OF_ENTERING_THREADS,
255                   NUMBER_OF_WAITING_THREADS);
256 
257             for (int i = 0; i < NUMBER_OF_WAITING_THREADS; i++) {
258                 lockCheck.notify(); // notify waiting threads one by one
259                 // now the notified WaitingTask has to be blocked on the lockCheck re-enter
260 
261                 // entry count: 1
262                 // count of threads waiting to enter:       NUMBER_OF_ENTERING_THREADS
263                 // count of threads waiting to re-enter:    i + 1
264                 // count of threads waiting to be notified: NUMBER_OF_WAITING_THREADS - i - 1
265                 check(lockCheck, Thread.currentThread(), 1,
266                       NUMBER_OF_ENTERING_THREADS + i + 1,
267                       NUMBER_OF_WAITING_THREADS  - i - 1);
268             }
269         }
270         joinThreads(wThreads);
271         joinThreads(eThreads);
272         setTestedMonitor(null);
273         log("### test3: finished " + vtag);
274     }
275 
276     static void test(boolean isVirtual) {
277         test0(isVirtual);
278         test1(isVirtual);
279         test2(isVirtual);
280         test3(isVirtual);
281     }
282 
283     public static void main(String args[]) {
284         log("\n### main: started\n");
285         check(lockCheck, null, 0, 0, 0);
286 
287         test(false); // test platform threads
288         test(true);  // test virtual threads
289 
290         check(lockCheck, null, 0, 0, 0);
291         if (getRes() > 0) {
292             throw new RuntimeException("Failed status returned from the agent");
293         }
294         log("\n### main: finished\n");
295     }
296 
297     static abstract class TestTask implements Runnable {
298         volatile boolean ready = false;
299         String name;
300 
301         public abstract void run();
302 
303         String getName() { return name; }
304         void setName(String name) { this.name = name; }
305 
306         void waitReady() {
307             try {
308                 while (!ready) {
309                     Thread.sleep(10);
310                 }
311             } catch (InterruptedException e) {
312                 throw new Error("Unexpected " + e);
313             }
314         }
315     }
316 
317     static class EnteringTask extends TestTask {
318         public void run() {
319             ready = true;
320             synchronized (lockCheck) {
321             }
322         }
323     }
324 
325     static class WaitingTask extends TestTask {
326         public void run() {
327             synchronized (lockCheck) {
328                 try {
329                     ready = true;
330                     // no protection against spurious wakeups here
331                     lockCheck.wait();
332                 } catch (InterruptedException e) {
333                     throw new Error("Unexpected " + e);
334                 }
335             }
336         }
337     }
338 }