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