1 /* 2 * Copyright (c) 2018, 2023, 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 26 /** 27 * @test 28 * @summary Basic tests for jdk.internal.vm.Continuation 29 * @requires vm.continuations 30 * @modules java.base/jdk.internal.vm 31 * @library /test/lib 32 * @build java.base/java.lang.StackWalkerHelper 33 * 34 * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -Xint Basic 35 * 36 * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -Xcomp -XX:TieredStopAtLevel=3 -XX:CompileOnly=jdk.internal.vm.Continuation::*,Basic::* Basic 37 * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -Xcomp -XX:-TieredCompilation -XX:CompileOnly=jdk.internal.vm.Continuation::*,Basic::* Basic 38 * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -Xcomp -XX:-TieredCompilation -XX:CompileOnly=jdk.internal.vm.Continuation::*,Basic::* -XX:CompileCommand=exclude,Basic.manyArgsDriver Basic 39 * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -Xcomp -XX:-TieredCompilation -XX:CompileOnly=jdk.internal.vm.Continuation::*,Basic::* -XX:CompileCommand=exclude,jdk/internal/vm/Continuation.enter Basic 40 * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -Xcomp -XX:-TieredCompilation -XX:CompileOnly=jdk.internal.vm.Continuation::*,Basic::* -XX:CompileCommand=inline,jdk/internal/vm/Continuation.run Basic 41 */ 42 43 /** 44 * @test 45 * @requires vm.continuations 46 * @requires vm.debug 47 * @modules java.base/jdk.internal.vm 48 * @library /test/lib 49 * @build java.base/java.lang.StackWalkerHelper 50 * 51 * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -XX:+VerifyStack -Xint Basic 52 * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -XX:+VerifyStack -Xcomp -XX:TieredStopAtLevel=3 -XX:CompileOnly=jdk.internal.vm.Continuation::*,Basic::* Basic 53 * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -XX:+VerifyStack -Xcomp -XX:-TieredCompilation -XX:CompileOnly=jdk.internal.vm.Continuation::*,Basic::* Basic 54 */ 55 56 import jdk.internal.vm.Continuation; 57 import jdk.internal.vm.ContinuationScope; 58 59 import jdk.test.lib.Platform; 60 61 import java.util.ArrayList; 62 import java.util.Arrays; 63 import java.util.List; 64 import java.util.stream.Collectors; 65 import java.util.stream.IntStream; 66 67 import org.testng.annotations.Test; 68 import org.testng.annotations.DataProvider; 69 import static org.testng.Assert.*; 70 71 import java.util.concurrent.atomic.AtomicInteger; 72 import java.util.concurrent.atomic.AtomicReference; 73 74 public class Basic { 75 static final ContinuationScope FOO = new ContinuationScope() {}; 76 77 @Test 78 public void test1() { 79 // Basic freeze and thaw 80 final AtomicInteger res = new AtomicInteger(0); 81 Continuation cont = new Continuation(FOO, ()-> { 82 double r = 0; 83 for (int k = 1; k < 20; k++) { 84 int x = 3; 85 String s = "abc"; 86 r += foo(k); 87 } 88 res.set((int)r); 89 }); 90 91 int i = 0; 92 while (!cont.isDone()) { 93 cont.run(); 94 System.gc(); 95 96 assertEquals(cont.isPreempted(), false); 97 98 List<String> frames = cont.stackWalker().walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); 99 assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "bar", "foo", "lambda$test1$0", "run", "enter0", "enter")); 100 } 101 assertEquals(res.get(), 247); 102 assertEquals(cont.isPreempted(), false); 103 } 104 105 static double foo(int a) { 106 long x = 8; 107 String s = "yyy"; 108 String r = bar(a + 1); 109 return Integer.parseInt(r)+1; 110 } 111 112 static final int DEPTH = 40; 113 static String bar(long b) { 114 double x = 9.99; 115 String s = "zzz"; 116 boolean res = Continuation.yield(FOO); 117 118 assertEquals(res, true); 119 120 deep(DEPTH); 121 122 long r = b+1; 123 return "" + r; 124 } 125 126 static String bar2(long b) { 127 double x = 9.99; 128 String s = "zzz"; 129 Continuation.yield(FOO); 130 131 long r = b+1; 132 return "" + r; 133 } 134 135 static void deep(int depth) { 136 if (depth > 1) { 137 deep(depth-1); 138 return; 139 } 140 141 StackWalker walker = StackWalker.getInstance(); 142 List<String> frames = walker.walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); 143 144 List<String> expected0 = new ArrayList<>(); 145 IntStream.range(0, DEPTH).forEach(i -> { expected0.add("deep"); }); 146 List<String> baseFrames = List.of("bar", "foo", "lambda$test1$0", "run", "enter0", "enter", "run", "test1"); 147 expected0.addAll(baseFrames); 148 149 assertEquals(frames.subList(0, DEPTH + baseFrames.size()), expected0); 150 151 walker = StackWalkerHelper.getInstance(FOO); 152 frames = walker.walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); 153 154 155 List<String> expected1 = new ArrayList<>(); 156 IntStream.range(0, DEPTH).forEach(i -> { expected1.add("deep"); }); 157 expected1.addAll(List.of("bar", "foo", "lambda$test1$0", "run", "enter0", "enter")); 158 assertEquals(frames, expected1); 159 } 160 161 static class LoomException extends RuntimeException { 162 public LoomException(String message) { 163 super(message); 164 } 165 } 166 167 static double fooThrow(int a) { 168 long x = 8; 169 String s = "yyy"; 170 String r = barThrow(a + 1); 171 return Integer.parseInt(r)+1; 172 } 173 174 static String barThrow(long b) { 175 double x = 9.99; 176 String s = "zzz"; 177 Continuation.yield(FOO); 178 179 long r = b+1; 180 181 if (true) 182 throw new LoomException("Loom exception!"); 183 return "" + r; 184 } 185 186 @Test 187 public void testException1() { 188 // Freeze and thaw with exceptions 189 Continuation cont = new Continuation(FOO, ()-> { 190 double r = 0; 191 for (int k = 1; k < 20; k++) { 192 int x = 3; 193 String s = "abc"; 194 r += fooThrow(k); 195 } 196 }); 197 198 cont.run(); 199 try { 200 cont.run(); 201 fail("Exception not thrown."); 202 } catch (LoomException e) { 203 assertEquals(e.getMessage(), "Loom exception!"); 204 // e.printStackTrace(); 205 StackTraceElement[] stes = e.getStackTrace(); 206 assertEquals(stes[0].getMethodName(), "barThrow"); 207 int index = -1; 208 for (int i=0; i<stes.length; i++) { 209 if (stes[i].getClassName().equals(Continuation.class.getName()) && stes[i].getMethodName().equals("enter")) { 210 index = i; 211 break; 212 } 213 } 214 assertTrue(index >= 0); 215 StackTraceElement last = stes[index]; 216 } 217 } 218 219 @Test 220 public void testManyArgs() { 221 // Methods with stack-passed arguments 222 final AtomicInteger res = new AtomicInteger(0); 223 Continuation cont = new Continuation(FOO, ()-> { 224 res.set((int)manyArgsDriver()); 225 }); 226 227 int i = 0; 228 while (!cont.isDone()) { 229 cont.run(); 230 System.gc(); 231 } 232 assertEquals(res.get(), 247); 233 } 234 235 static double manyArgsDriver() { 236 double r = 0; 237 for (int k = 1; k < 20; k++) { 238 int x = 3; 239 String s = "abc"; 240 241 r += fooMany(k, 242 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 243 1.0, 2.0, 3.0, 4.0, 5.0, 6.0f, 7.0f, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0f, 15.0f, 16.0f, 17.0, 18.0, 19.0, 20.0, 244 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020); 245 } 246 return r; 247 } 248 249 static double fooMany(int a, 250 int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10, int x11, int x12, 251 int x13, int x14, int x15, int x16, int x17, int x18, int x19, int x20, 252 double f1, double f2, double f3, double f4, double f5, float f6, float f7, double f8, double f9, double f10, 253 double f11, double f12, double f13, float f14, float f15, float f16, double f17, double f18, double f19, double f20, 254 Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7, Object o8, Object o9, Object o10, 255 Object o11, Object o12, Object o13, Object o14, Object o15, Object o16, Object o17, Object o18, Object o19, Object o20) { 256 long x = 8; 257 String s = "yyy"; 258 String r = barMany(a + 1, 259 x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, 260 f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, 261 o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11, o12, o13, o14, o15, o16, o17, o18, o19, o20); 262 return Integer.parseInt(r)+1; 263 } 264 265 static String barMany(long b, 266 int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10, int x11, int x12, 267 int x13, int x14, int x15, int x16, int x17, int x18, int x19, int x20, 268 double f1, double f2, double f3, double f4, double f5, float f6, float f7, double f8, double f9, double f10, 269 double f11, double f12, double f13, float f14, float f15, float f16, double f17, double f18, double f19, double f20, 270 Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7, Object o8, Object o9, Object o10, 271 Object o11, Object o12, Object o13, Object o14, Object o15, Object o16, Object o17, Object o18, Object o19, Object o20) { 272 double x = 9.99; 273 String s = "zzz"; 274 for (int i=0; i<2; i++) { 275 Continuation.yield(FOO); 276 } 277 long r = b+1; 278 return "" + r; 279 } 280 281 @Test 282 public void testPinnedMonitor() { 283 if (Platform.isX64() || Platform.isAArch64()) return; 284 285 // Test pinning due to held monitor 286 final AtomicReference<Continuation.Pinned> res = new AtomicReference<>(); 287 288 Continuation cont = new Continuation(FOO, ()-> { 289 syncFoo(1); 290 }) { 291 @Override 292 protected void onPinned(Continuation.Pinned reason) { 293 assert Continuation.isPinned(FOO); 294 res.set(reason); 295 } 296 }; 297 298 cont.run(); 299 assertEquals(res.get(), Continuation.Pinned.MONITOR); 300 boolean isDone = cont.isDone(); 301 assertEquals(isDone, true); 302 } 303 304 static double syncFoo(int a) { 305 long x = 8; 306 String s = "yyy"; 307 String r; 308 synchronized(FOO) { 309 r = bar2(a + 1); 310 } 311 return Integer.parseInt(r)+1; 312 } 313 314 @Test 315 public void testNotPinnedMonitor() { 316 final AtomicReference<Continuation.Pinned> res = new AtomicReference<>(); 317 318 Continuation cont = new Continuation(FOO, ()-> { 319 noSyncFoo(1); 320 }) { 321 @Override 322 protected void onPinned(Continuation.Pinned reason) { 323 assert Continuation.isPinned(FOO); 324 res.set(reason); 325 } 326 }; 327 328 cont.run(); 329 boolean isDone = cont.isDone(); 330 assertEquals(res.get(), null); 331 assertEquals(isDone, false); 332 } 333 334 static double noSyncFoo(int a) { 335 long x = 7; 336 synchronized(FOO) { 337 x += FOO.getClass().getName().contains("FOO") ? 1 : 0; 338 } 339 String s = "yyy"; 340 String r = bar2(a + 1); 341 return Integer.parseInt(r)+1; 342 } 343 344 @Test 345 public void testPinnedCriticalSection() { 346 // pinning due to critical section 347 final AtomicReference<Continuation.Pinned> res = new AtomicReference<>(); 348 349 Continuation cont = new Continuation(FOO, ()-> { 350 csFoo(1); 351 }) { 352 @Override 353 protected void onPinned(Continuation.Pinned reason) { 354 res.set(reason); 355 } 356 }; 357 358 cont.run(); 359 assertEquals(res.get(), Continuation.Pinned.CRITICAL_SECTION); 360 } 361 362 static double csFoo(int a) { 363 long x = 8; 364 String s = "yyy"; 365 String r; 366 Continuation.pin(); 367 try { 368 assert Continuation.isPinned(FOO); 369 r = bar2(a + 1); 370 } finally { 371 Continuation.unpin(); 372 } 373 return Integer.parseInt(r)+1; 374 } 375 376 @Test 377 public void testPinnedNative() { 378 // pinning due to native method 379 final AtomicReference<Continuation.Pinned> res = new AtomicReference<>(); 380 381 Continuation cont = new Continuation(FOO, ()-> { 382 nativeFoo(1); 383 }) { 384 @Override 385 protected void onPinned(Continuation.Pinned reason) { 386 res.set(reason); 387 } 388 }; 389 390 cont.run(); 391 assertEquals(res.get(), Continuation.Pinned.NATIVE); 392 } 393 394 static double nativeFoo(int a) { 395 try { 396 int x = 8; 397 String s = "yyy"; 398 return nativeBar(x); 399 } catch (Exception e) { 400 throw new AssertionError(e); 401 } 402 } 403 404 static int nativeBaz(int b) { 405 double x = 9.99; 406 String s = "zzz"; 407 assert Continuation.isPinned(FOO); 408 boolean res = Continuation.yield(FOO); 409 assert res == false; 410 411 return b+1; 412 } 413 414 private static native int nativeBar(int x); 415 416 static { 417 System.loadLibrary("BasicJNI"); 418 } 419 }