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 }