1 /* 2 * Copyright (c) 2020, 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 8284161 8289284 8322846 27 * @summary Basic test of debugging option to trace pinned threads 28 * @requires vm.continuations 29 * @library /test/lib 30 * @run junit/othervm -Djdk.tracePinnedThreads=full TracePinnedThreads 31 * @run junit/othervm -Djdk.tracePinnedThreads=short TracePinnedThreads 32 */ 33 34 import java.io.ByteArrayOutputStream; 35 import java.io.PrintStream; 36 import java.time.Duration; 37 import java.util.concurrent.Executors; 38 import java.util.concurrent.locks.LockSupport; 39 40 import jdk.test.lib.thread.VThreadRunner; 41 import org.junit.jupiter.api.Test; 42 import static org.junit.jupiter.api.Assertions.*; 43 44 class TracePinnedThreads { 45 static final Object lock = new Object(); 46 47 /** 48 * Parks current thread for 1 second. 49 */ 50 private static void park() { 51 long nanos = Duration.ofSeconds(1).toNanos(); 52 LockSupport.parkNanos(nanos); 53 } 54 55 /** 56 * Invokes the park method through a native frame to park the current 57 * thread for 1 second. 58 */ 59 private static native void invokePark(); 60 61 /** 62 * Test parking inside synchronized block. 63 */ 64 @Test 65 void testPinnedCausedBySynchronizedBlock() throws Exception { 66 String output = run(() -> { 67 synchronized (lock) { 68 park(); 69 } 70 }); 71 assertContains(output, "reason:MONITOR"); 72 assertContains(output, "<== monitors:1"); 73 } 74 75 /** 76 * Test parking with native frame on stack. 77 */ 78 @Test 79 void testPinnedCausedByNativeMethod() throws Exception { 80 System.loadLibrary("TracePinnedThreads"); 81 String output = run(() -> invokePark()); 82 assertContains(output, "reason:NATIVE"); 83 assertContains(output, "(Native Method)"); 84 } 85 86 /** 87 * Test parking in class initializer. 88 */ 89 @Test 90 void testPinnedCausedByClassInitializer() throws Exception { 91 class C { 92 static { 93 park(); 94 } 95 } 96 String output = run(C::new); 97 assertContains(output, "reason:NATIVE"); 98 assertContains(output, "<clinit>"); 99 } 100 101 /** 102 * Test contention writing to System.out when pinned. The test creates four threads 103 * that write to System.out when pinned, this is enough to potentially deadlock 104 * without the changes in JDK-8322846. 105 */ 106 @Test 107 void testContention() throws Exception { 108 // use several classes to avoid duplicate stack traces 109 class C1 { 110 synchronized void print() { 111 System.out.println("hello"); 112 } 113 } 114 class C2 { 115 synchronized void print() { 116 System.out.println("hello"); 117 } 118 } 119 class C3 { 120 synchronized void print() { 121 System.out.println("hello"); 122 } 123 } 124 class C4 { 125 synchronized void print() { 126 System.out.println("hello"); 127 } 128 } 129 130 try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { 131 executor.submit(() -> { 132 new C1().print(); 133 }); 134 executor.submit(() -> { 135 new C2().print(); 136 }); 137 executor.submit(() -> { 138 new C3().print(); 139 }); 140 executor.submit(() -> { 141 new C4().print(); 142 }); 143 } 144 } 145 146 /** 147 * Run a task in a virtual thread, returning a String with any output printed 148 * to standard output. 149 */ 150 private static String run(Runnable task) throws Exception { 151 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 152 PrintStream original = System.out; 153 System.setOut(new PrintStream(baos, true)); 154 try { 155 VThreadRunner.run(task::run); 156 } finally { 157 System.setOut(original); 158 } 159 String output = new String(baos.toByteArray()); 160 System.out.println(output); 161 return output; 162 } 163 164 /** 165 * Tests that s1 contains s2. 166 */ 167 private static void assertContains(String s1, String s2) { 168 assertTrue(s1.contains(s2), s2 + " not found!!!"); 169 } 170 171 /** 172 * Tests that s1 does not contain s2. 173 */ 174 private static void assertDoesNotContain(String s1, String s2) { 175 assertFalse(s1.contains(s2), s2 + " found!!"); 176 } 177 }