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 LotsOfContendedMonitorEnter
29 */
30
31 import java.util.concurrent.CountDownLatch;
32 import jdk.test.lib.thread.VThreadRunner;
33
34 public class LotsOfContendedMonitorEnter {
35
36 public static void main(String[] args) throws Exception {
37 int depth;
38 if (args.length > 0) {
39 depth = Integer.parseInt(args[0]);
40 } else {
41 depth = 1024;
42 }
43 VThreadRunner.run(() -> testContendedEnter(depth));
44 }
45
46 /**
47 * Enter the monitor for a new object, racing with another virtual thread that
48 * attempts to enter around the same time, then repeat to the given depth.
49 */
50 private static void testContendedEnter(int depthRemaining) throws Exception {
51 if (depthRemaining > 0) {
52 var lock = new Object();
53
54 // start thread to enter monitor for brief period, then enters again when signalled
55 var started = new CountDownLatch(1);
56 var signal = new CountDownLatch(1);
57 var thread = Thread.ofVirtual().start(() -> {
58 started.countDown();
59
60 // enter, may be contended
61 synchronized (lock) {
62 Thread.onSpinWait();
63 }
64
65 // wait to be signalled
66 try {
67 signal.await();
68 } catch (InterruptedException e) { }
69
70 // enter again, this will block until the main thread releases
71 synchronized (lock) {
72 // do nothing
73 }
74 });
75 try {
76 // wait for thread to start
77 started.await();
78
79 // enter, may be contended
80 synchronized (lock) {
81 // signal thread to enter monitor again, it should block
82 signal.countDown();
83 await(thread, Thread.State.BLOCKED);
84 testContendedEnter(depthRemaining - 1);
85 }
86 } finally {
87 thread.join();
88 }
89 }
90 }
91
92 /**
93 * Waits for the given thread to reach a given state.
94 */
95 private static void await(Thread thread, Thread.State expectedState) {
96 Thread.State state = thread.getState();
97 while (state != expectedState) {
98 assert state != Thread.State.TERMINATED : "Thread has terminated";
99 Thread.yield();
100 state = thread.getState();
101 }
102 }
103 }