1 /*
 2  * Copyright (c) 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  * @test
26  * @summary Stress test parking and unparking
27  * @requires vm.debug != true
28  * @run main/othervm ParkALot 500000
29  */
30 
31 /*
32  * @test
33  * @requires vm.debug == true
34  * @run main/othervm ParkALot 100000
35  */
36 
37 import java.time.Instant;
38 import java.util.concurrent.Executors;
39 import java.util.concurrent.ThreadFactory;
40 import java.util.concurrent.locks.LockSupport;
41 
42 public class ParkALot {
43     private static final int ITERATIONS = 1_000_000;
44 
45     public static void main(String[] args) {
46         int iterations;
47         if (args.length > 0) {
48             iterations = Integer.parseInt(args[0]);
49         } else {
50             iterations = ITERATIONS;
51         }
52 
53         int maxThreads = Math.clamp(Runtime.getRuntime().availableProcessors() / 2, 1, 4);
54         for (int nthreads = 1; nthreads <= maxThreads; nthreads++) {
55             System.out.format("%s %d thread(s) ...%n", Instant.now(), nthreads);
56             ThreadFactory factory = Thread.ofPlatform().factory();
57             try (var executor = Executors.newThreadPerTaskExecutor(factory)) {
58                 for (int i = 0; i < nthreads; i++) {
59                     executor.submit(() -> parkALot(iterations));
60                 }
61             }
62             System.out.format("%s %d thread(s) done%n", Instant.now(), nthreads);
63         }
64     }
65 
66     /**
67      * Creates a virtual thread that alternates between untimed and timed parking.
68      * A platform thread spins unparking the virtual thread.
69      */
70     private static void parkALot(int iterations) {
71         Thread vthread = Thread.ofVirtual().start(() -> {
72             int i = 0;
73             boolean timed = false;
74             while (i < iterations) {
75                 if (timed) {
76                     LockSupport.parkNanos(Long.MAX_VALUE);
77                     timed = false;
78                 } else {
79                     LockSupport.park();
80                     timed = true;
81                 }
82                 i++;
83             }
84         });
85 
86         Thread.State state;
87         while ((state = vthread.getState()) != Thread.State.TERMINATED) {
88             if (state == Thread.State.WAITING || state == Thread.State.TIMED_WAITING) {
89                 LockSupport.unpark(vthread);
90             } else {
91                 Thread.yield();
92             }
93         }
94     }
95 }