1 /*
  2  * Copyright (c) 2016, 2020, Red Hat, Inc. 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  * @test
 27  * @summary Tests for crash/assert when attaching init thread during shutdown
 28  * @requires vm.gc.Shenandoah
 29  * @library /test/lib
 30  * @modules java.base/jdk.internal.misc
 31  *          java.management
 32  * @run driver/timeout=480 TestEvilSyncBug
 33  */
 34 
 35 import java.util.*;
 36 import java.util.concurrent.*;
 37 import java.util.concurrent.locks.*;
 38 
 39 import jdk.test.lib.process.ProcessTools;
 40 import jdk.test.lib.process.OutputAnalyzer;
 41 
 42 public class TestEvilSyncBug {
 43 
 44     private static final int NUM_RUNS = 100;
 45 
 46     static Thread[] hooks = new MyHook[10000];
 47 
 48     public static void main(String[] args) throws Exception {
 49         if (args.length > 0) {
 50             test();
 51         } else {
 52             // Use 1/4 of available processors to avoid over-saturation.
 53             int numJobs = Math.max(1, Runtime.getRuntime().availableProcessors() / 4);
 54             ExecutorService pool = Executors.newFixedThreadPool(numJobs);
 55             Future<?>[] fs = new Future<?>[NUM_RUNS];
 56 
 57             for (int c = 0; c < NUM_RUNS; c++) {
 58                 Callable<Void> task = () -> {
 59                     OutputAnalyzer output = ProcessTools.executeLimitedTestJava("-Xms128m",
 60                             "-Xmx128m",
 61                             "-XX:+UnlockExperimentalVMOptions",
 62                             "-XX:+UnlockDiagnosticVMOptions",
 63                             "-XX:+UseShenandoahGC",
 64                             "-XX:ShenandoahGCHeuristics=aggressive",
 65                             "TestEvilSyncBug", "test");
 66                     output.shouldHaveExitValue(0);
 67                     return null;
 68                 };
 69                 fs[c] = pool.submit(task);
 70             }
 71 
 72             for (Future<?> f : fs) {
 73                 f.get();
 74             }
 75 
 76             pool.shutdown();
 77             pool.awaitTermination(1, TimeUnit.HOURS);
 78         }
 79     }
 80 
 81     private static void test() throws Exception {
 82 
 83         for (int t = 0; t < hooks.length; t++) {
 84             hooks[t] = new MyHook();
 85         }
 86 
 87         ExecutorService service = Executors.newFixedThreadPool(
 88                 2,
 89                 r -> {
 90                     Thread t = new Thread(r);
 91                     t.setDaemon(true);
 92                     return t;
 93                 }
 94         );
 95 
 96         List<Future<?>> futures = new ArrayList<>();
 97         for (int c = 0; c < 100; c++) {
 98             Runtime.getRuntime().addShutdownHook(hooks[c]);
 99             final Test[] tests = new Test[1000];
100             for (int t = 0; t < tests.length; t++) {
101                 tests[t] = new Test();
102             }
103 
104             Future<?> f1 = service.submit(() -> {
105                 Runtime.getRuntime().addShutdownHook(new MyHook());
106                 IntResult2 r = new IntResult2();
107                 for (Test test : tests) {
108                     test.RL_Us(r);
109                 }
110             });
111             Future<?> f2 = service.submit(() -> {
112                 Runtime.getRuntime().addShutdownHook(new MyHook());
113                 for (Test test : tests) {
114                     test.WLI_Us();
115                 }
116             });
117 
118             futures.add(f1);
119             futures.add(f2);
120         }
121 
122         for (Future<?> f : futures) {
123             f.get();
124         }
125     }
126 
127     public static class IntResult2 {
128         int r1, r2;
129     }
130 
131     public static class Test {
132         final StampedLock lock = new StampedLock();
133 
134         int x, y;
135 
136         public void RL_Us(IntResult2 r) {
137             StampedLock lock = this.lock;
138             long stamp = lock.readLock();
139             r.r1 = x;
140             r.r2 = y;
141             lock.unlock(stamp);
142         }
143 
144         public void WLI_Us() {
145             try {
146                 StampedLock lock = this.lock;
147                 long stamp = lock.writeLockInterruptibly();
148                 x = 1;
149                 y = 2;
150                 lock.unlock(stamp);
151             } catch (InterruptedException e) {
152                 throw new RuntimeException(e);
153             }
154         }
155     }
156 
157     private static class MyHook extends Thread {
158         @Override
159         public void run() {
160             try {
161                 Thread.sleep(10);
162             } catch (Exception e) {}
163         }
164     }
165 
166 }