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 import jdk.test.lib.Asserts;
24 import jdk.test.lib.Utils;
25 import jdk.test.lib.process.ProcessTools;
26 import jdk.test.lib.process.OutputAnalyzer;
27
28 import java.lang.reflect.Constructor;
29 import java.lang.reflect.InvocationTargetException;
30 import java.util.List;
31 import java.util.concurrent.atomic.AtomicReference;
32 import java.util.concurrent.ExecutorService;
33 import java.util.concurrent.Executor;
34 import java.util.concurrent.Executors;
35 import java.util.concurrent.ThreadFactory;
36 import java.util.regex.Matcher;
37 import java.util.regex.Pattern;
38
39 /*
40 * Tests that JNI monitors work correctly with virtual threads,
41 * There are multiple test scenarios that we check using unified logging output
42 * (both positive and negative tests). Each test case is handled by its own @-test
43 * definition so that we can run each sub-test independently.
44 *
45 * The original bug was only discovered because the ForkJoinPool worker thread terminated
46 * and trigerred an assertion failure. So we use a custom scheduler to give us control.
95 * @requires vm.continuations
96 * @run driver JNIMonitor MissingUnlockWithThrow
97 */
98
99 /**
100 * @test id=multiMissingUnlockWithThrow
101 * @bug 8327743
102 * @summary Don't do the unlock and exit by throwing, by multiple threads
103 * @library /test/lib
104 * @modules java.base/java.lang:+open
105 * @requires vm.continuations
106 * @run driver JNIMonitor MultiMissingUnlockWithThrow
107 */
108
109 public class JNIMonitor {
110
111 public static void main(String[] args) throws Exception {
112 String test = args[0];
113 String[] cmdArgs = new String[] {
114 "-Djava.library.path=" + Utils.TEST_NATIVE_PATH,
115 // Grant access to ThreadBuilders$VirtualThreadBuilder
116 "--add-opens=java.base/java.lang=ALL-UNNAMED",
117 // Enable the JNI warning
118 "-Xcheck:jni",
119 "-Xlog:jni=debug",
120 // Enable thread termination logging as a visual cross-check
121 "-Xlog:thread+os=info",
122 // We only count monitors in LM_LEGACY mode
123 "-XX:LockingMode=1",
124 // Disable compact headers since that switches locking mode to LM_LIGHTWEIGHT
125 "-XX:-UseCompactObjectHeaders",
126 "JNIMonitor$" + test,
127 };
128 OutputAnalyzer oa = ProcessTools.executeTestJava(cmdArgs);
129 oa.shouldHaveExitValue(0);
130 oa.stdoutShouldMatch(terminated);
131
132 switch(test) {
133 case "Normal":
134 case "MultiNormal":
135 oa.stdoutShouldNotMatch(stillLocked);
182 }
183 if (found != expected) {
184 throw new RuntimeException("Checking for pattern \"" + pattern + "\": expected "
185 + expected + " but found " + found);
186 }
187 }
188
189
190 // straight-forward interface to JNI monitor functions
191 static native int monitorEnter(Object o);
192 static native int monitorExit(Object o);
193
194 // Isolate the native library loading to the actual test cases, not the class that
195 // jtreg Driver will load and execute.
196 static class TestBase {
197
198 static {
199 System.loadLibrary("JNIMonitor");
200 }
201
202 // This gives us a way to control the scheduler used for our virtual threads. The test
203 // only works as intended when the virtual threads run on the same carrier thread (as
204 // that carrier maintains ownership of the monitor if the virtual thread fails to unlock it).
205 // The original issue was also only discovered due to the carrier thread terminating
206 // unexpectedly, so we can force that condition too by shutting down our custom scheduler.
207 private static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) {
208 Thread.Builder.OfVirtual builder = Thread.ofVirtual();
209 try {
210 Class<?> clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder");
211 Constructor<?> ctor = clazz.getDeclaredConstructor(Executor.class);
212 ctor.setAccessible(true);
213 return (Thread.Builder.OfVirtual) ctor.newInstance(scheduler);
214 } catch (InvocationTargetException e) {
215 Throwable cause = e.getCause();
216 if (cause instanceof RuntimeException re) {
217 throw re;
218 }
219 throw new RuntimeException(e);
220 } catch (Exception e) {
221 throw new RuntimeException(e);
222 }
223 }
224
225 static void runTest(int nThreads, boolean skipUnlock, boolean throwOnExit) throws Throwable {
226 final Object[] monitors = new Object[nThreads];
227 for (int i = 0; i < nThreads; i++) {
228 monitors[i] = new Object();
229 }
230 final AtomicReference<Throwable> exception = new AtomicReference();
231 // Ensure all our VT's operate of the same carrier, sequentially.
232 ExecutorService scheduler = Executors.newSingleThreadExecutor();
233 ThreadFactory factory = virtualThreadBuilder(scheduler).factory();
234 for (int i = 0 ; i < nThreads; i++) {
235 Object monitor = skipUnlock ? monitors[i] : monitors[0];
236 Thread th = factory.newThread(() -> {
237 try {
238 int res = monitorEnter(monitor);
239 Asserts.assertTrue(res == 0, "monitorEnter should return 0.");
240 Asserts.assertTrue(Thread.holdsLock(monitor), "monitor should be owned");
241 Thread.yield();
242 if (!skipUnlock) {
243 res = monitorExit(monitor);
244 Asserts.assertTrue(res == 0, "monitorExit should return 0.");
245 Asserts.assertFalse(Thread.holdsLock(monitor), "monitor should be unowned");
246 }
247 } catch (Throwable t) {
248 exception.set(t);
249 }
250 if (throwOnExit) {
251 throw new RuntimeException(throwMsg);
252 }
253 });
|
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 import jdk.test.lib.Asserts;
24 import jdk.test.lib.Utils;
25 import jdk.test.lib.process.ProcessTools;
26 import jdk.test.lib.process.OutputAnalyzer;
27 import jdk.test.lib.thread.VThreadScheduler;
28
29 import java.lang.reflect.Constructor;
30 import java.lang.reflect.InvocationTargetException;
31 import java.util.List;
32 import java.util.concurrent.atomic.AtomicReference;
33 import java.util.concurrent.ExecutorService;
34 import java.util.concurrent.Executor;
35 import java.util.concurrent.Executors;
36 import java.util.concurrent.ThreadFactory;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39
40 /*
41 * Tests that JNI monitors work correctly with virtual threads,
42 * There are multiple test scenarios that we check using unified logging output
43 * (both positive and negative tests). Each test case is handled by its own @-test
44 * definition so that we can run each sub-test independently.
45 *
46 * The original bug was only discovered because the ForkJoinPool worker thread terminated
47 * and trigerred an assertion failure. So we use a custom scheduler to give us control.
96 * @requires vm.continuations
97 * @run driver JNIMonitor MissingUnlockWithThrow
98 */
99
100 /**
101 * @test id=multiMissingUnlockWithThrow
102 * @bug 8327743
103 * @summary Don't do the unlock and exit by throwing, by multiple threads
104 * @library /test/lib
105 * @modules java.base/java.lang:+open
106 * @requires vm.continuations
107 * @run driver JNIMonitor MultiMissingUnlockWithThrow
108 */
109
110 public class JNIMonitor {
111
112 public static void main(String[] args) throws Exception {
113 String test = args[0];
114 String[] cmdArgs = new String[] {
115 "-Djava.library.path=" + Utils.TEST_NATIVE_PATH,
116 // Need to open java.lang to use VThreadScheduler.virtualThreadBuilder
117 "--add-opens=java.base/java.lang=ALL-UNNAMED",
118 // Enable the JNI warning
119 "-Xcheck:jni",
120 "-Xlog:jni=debug",
121 // Enable thread termination logging as a visual cross-check
122 "-Xlog:thread+os=info",
123 // We only count monitors in LM_LEGACY mode
124 "-XX:LockingMode=1",
125 // Disable compact headers since that switches locking mode to LM_LIGHTWEIGHT
126 "-XX:-UseCompactObjectHeaders",
127 "JNIMonitor$" + test,
128 };
129 OutputAnalyzer oa = ProcessTools.executeTestJava(cmdArgs);
130 oa.shouldHaveExitValue(0);
131 oa.stdoutShouldMatch(terminated);
132
133 switch(test) {
134 case "Normal":
135 case "MultiNormal":
136 oa.stdoutShouldNotMatch(stillLocked);
183 }
184 if (found != expected) {
185 throw new RuntimeException("Checking for pattern \"" + pattern + "\": expected "
186 + expected + " but found " + found);
187 }
188 }
189
190
191 // straight-forward interface to JNI monitor functions
192 static native int monitorEnter(Object o);
193 static native int monitorExit(Object o);
194
195 // Isolate the native library loading to the actual test cases, not the class that
196 // jtreg Driver will load and execute.
197 static class TestBase {
198
199 static {
200 System.loadLibrary("JNIMonitor");
201 }
202
203 static void runTest(int nThreads, boolean skipUnlock, boolean throwOnExit) throws Throwable {
204 final Object[] monitors = new Object[nThreads];
205 for (int i = 0; i < nThreads; i++) {
206 monitors[i] = new Object();
207 }
208 final AtomicReference<Throwable> exception = new AtomicReference();
209 // Ensure all our VT's operate of the same carrier, sequentially.
210 // This gives us a way to control the scheduler used for our virtual threads. The test
211 // only works as intended when the virtual threads run on the same carrier thread (as
212 // that carrier maintains ownership of the monitor if the virtual thread fails to unlock it).
213 // The original issue was also only discovered due to the carrier thread terminating
214 // unexpectedly, so we can force that condition too by shutting down our custom scheduler.
215 ExecutorService scheduler = Executors.newSingleThreadExecutor();
216 ThreadFactory factory = VThreadScheduler.virtualThreadBuilder(scheduler).factory();
217 for (int i = 0 ; i < nThreads; i++) {
218 Object monitor = skipUnlock ? monitors[i] : monitors[0];
219 Thread th = factory.newThread(() -> {
220 try {
221 int res = monitorEnter(monitor);
222 Asserts.assertTrue(res == 0, "monitorEnter should return 0.");
223 Asserts.assertTrue(Thread.holdsLock(monitor), "monitor should be owned");
224 Thread.yield();
225 if (!skipUnlock) {
226 res = monitorExit(monitor);
227 Asserts.assertTrue(res == 0, "monitorExit should return 0.");
228 Asserts.assertFalse(Thread.holdsLock(monitor), "monitor should be unowned");
229 }
230 } catch (Throwable t) {
231 exception.set(t);
232 }
233 if (throwOnExit) {
234 throw new RuntimeException(throwMsg);
235 }
236 });
|