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 287 //test(true); // test virtual threads 288 289 check(lockCheck, null, 0, 0, 0); 290 if (getRes() > 0) { 291 throw new RuntimeException("Failed status returned from the agent"); 292 } 293 log("\n### main: finished\n"); 294 } 295 296 static abstract class TestTask implements Runnable { 297 volatile boolean ready = false; 298 String name; 299 300 public abstract void run(); 301 302 String getName() { return name; } 303 void setName(String name) { this.name = name; } 304 305 void waitReady() { 306 try { 307 while (!ready) { 308 Thread.sleep(10); 309 } 310 } catch (InterruptedException e) { 311 throw new Error("Unexpected " + e); 312 } 313 } 314 } 315 316 static class EnteringTask extends TestTask { 317 public void run() { 318 ready = true; 319 synchronized (lockCheck) { 320 } 321 } 322 } 323 324 static class WaitingTask extends TestTask { 325 public void run() { 326 synchronized (lockCheck) { 327 try { 328 ready = true; 329 // no protection against spurious wakeups here 330 lockCheck.wait(); 331 } catch (InterruptedException e) { 332 throw new Error("Unexpected " + e); 333 } 334 } 335 } 336 } 337 }