1 /* 2 * Copyright (c) 2024, 2025, 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 id=default 26 * @summary Test virtual threads entering a lot of monitors with contention 27 * @library /test/lib 28 * @run main/timeout=480 LotsOfContendedMonitorEnter 29 */ 30 31 import java.time.Instant; 32 import java.util.concurrent.atomic.AtomicReference; 33 import java.util.concurrent.CountDownLatch; 34 import jdk.test.lib.thread.VThreadRunner; 35 36 public class LotsOfContendedMonitorEnter { 37 static int depth; 38 39 public static void main(String[] args) throws Exception { 40 if (args.length > 0) { 41 depth = Integer.parseInt(args[0]); 42 } else { 43 depth = 1024; 44 } 45 46 var exRef = new AtomicReference<Throwable>(); 47 var thread = Thread.ofVirtual().start(() -> { 48 try { 49 testContendedEnter(depth); 50 } catch (Exception e) { 51 exRef.set(e); 52 } 53 }); 54 thread.join(); 55 assert exRef.get() == null; 56 } 57 58 /** 59 * Enter the monitor for a new object, racing with another virtual thread that 60 * attempts to enter around the same time, then repeat to the given depth. 61 */ 62 private static void testContendedEnter(int depthRemaining) throws Exception { 63 boolean doLog = depthRemaining % 10 == 0; 64 if (depthRemaining > 0) { 65 var lock = new Object(); 66 67 // start thread to enter monitor for brief period, then enters again when signalled 68 var started = new CountDownLatch(1); 69 var signal = new CountDownLatch(1); 70 var thread = Thread.ofVirtual().start(() -> { 71 started.countDown(); 72 73 // enter, may be contended 74 synchronized (lock) { 75 Thread.onSpinWait(); 76 } 77 78 // wait to be signalled 79 try { 80 signal.await(); 81 } catch (InterruptedException e) { } 82 83 // enter again, this will block until the main thread releases 84 synchronized (lock) { 85 // do nothing 86 } 87 }); 88 try { 89 // wait for thread to start 90 started.await(); 91 92 // enter, may be contended 93 synchronized (lock) { 94 // signal thread to enter monitor again, it should block 95 signal.countDown(); 96 await(thread, Thread.State.BLOCKED); 97 if (doLog) { 98 System.out.println(Instant.now() + " => at depth: " + (depth - depthRemaining)); 99 } 100 testContendedEnter(depthRemaining - 1); 101 } 102 } finally { 103 thread.join(); 104 } 105 } 106 if (doLog) { 107 System.out.println(Instant.now() + " => returning from depth: " + (depth - depthRemaining)); 108 } 109 } 110 111 /** 112 * Waits for the given thread to reach a given state. 113 */ 114 private static void await(Thread thread, Thread.State expectedState) { 115 Thread.State state = thread.getState(); 116 while (state != expectedState) { 117 assert state != Thread.State.TERMINATED : "Thread has terminated"; 118 Thread.yield(); 119 state = thread.getState(); 120 } 121 } 122 }