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 }