1 /*
  2  * Copyright (c) 2021, 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  * @test
 26  * @summary Stress test for ExtentLocal
 27  * @modules jdk.incubator.concurrent
 28  * @compile --enable-preview -source ${jdk.version} Stress.java
 29  * @run testng/othervm/timeout=300 -XX:-TieredCompilation --enable-preview Stress
 30  * @run testng/othervm/timeout=300 --enable-preview Stress
 31  */
 32 
 33 import jdk.incubator.concurrent.ExtentLocal;
 34 import jdk.incubator.concurrent.StructuredTaskScope;
 35 import jdk.incubator.concurrent.StructureViolationException;
 36 import java.util.concurrent.ThreadFactory;
 37 import org.testng.annotations.Test;
 38 import static org.testng.Assert.*;
 39 
 40 public class Stress {
 41 
 42     ExtentLocal<Integer> sl1 = ExtentLocal.newInstance();
 43     ExtentLocal<Integer> sl2 = ExtentLocal.newInstance();
 44 
 45     static final ExtentLocal<ThreadFactory> factory = ExtentLocal.newInstance();
 46     static final ExtentLocal.Carrier platformFactoryCarrier = ExtentLocal.where(factory, Thread.ofPlatform().factory());
 47     static final ExtentLocal.Carrier virtualFactoryCarrier = ExtentLocal.where(factory, Thread.ofVirtual().factory());
 48 
 49     final ExtentLocal<Integer>[] scopeLocals;
 50 
 51     Stress() {
 52         scopeLocals = new ExtentLocal[500];
 53         for (int i = 0; i < scopeLocals.length; i++) {
 54             scopeLocals[i] = ExtentLocal.newInstance();
 55         }
 56     }
 57 
 58     private class MyBanger implements Runnable {
 59         final ExtentLocal.Binder binder;
 60         boolean shouldRunOutOfMemory;
 61         boolean failed = false;
 62 
 63         MyBanger(ExtentLocal.Binder binder, boolean shouldRunOutOfMemory) {
 64             this.binder = binder;
 65             this.shouldRunOutOfMemory = shouldRunOutOfMemory;
 66         }
 67 
 68         volatile int a[][] = new int[10000][];
 69 
 70         public void runOutOfMemory(int base, int size) {
 71             for (int i = base; i < a.length; i++) {
 72                 try {
 73                     a[i] = new int[size];
 74                 } catch (OutOfMemoryError e) {
 75                     size /= 2;
 76                     if (size == 0) {
 77                         return;
 78                     }
 79                 }
 80             }
 81         }
 82 
 83         public void run() {
 84             int n = sl1.get();
 85             try {
 86                 ExtentLocal.where(sl1, n + 1).run(this);
 87             } catch (StackOverflowError e) {
 88                 if (sl1.get() != n) {
 89                     failed = true;
 90                 }
 91             }
 92             if (shouldRunOutOfMemory) {
 93                 runOutOfMemory(0, 0x1000_0000);
 94             }
 95 
 96             // Trigger a StructureViolationException
 97             binder.close();
 98         }
 99 
100     }
101 
102     public void stackOverflow() {
103         ExtentLocal.Binder binder = sl2.bind(99);
104         try {
105             var myBanger = new MyBanger(binder, false);
106             try {
107                 ExtentLocal.where(sl1, 0, myBanger);
108             } catch (RuntimeException e) {
109                 assertFalse(sl1.isBound());
110             } finally {
111                 binder.close();
112             }
113             assertFalse(myBanger.failed);
114         } finally {
115             binder.close();
116         }
117     }
118 
119     private int deepBindings(int depth) {
120         try {
121             if (depth > 0) {
122                 try (var unused = scopeLocals[depth].bind(depth)) {
123                     var vx = scopeLocals[depth].get();
124                     return ExtentLocal.where(sl1, sl1.get() + 1)
125                             .where(scopeLocals[depth], scopeLocals[depth].get() * 2)
126                             .call(() -> scopeLocals[depth].get() + deepBindings(depth - 1) + sl1.get());
127                 }
128             } else {
129                 return sl2.get();
130             }
131         } catch (Exception foo) {
132             return 0;
133         }
134     }
135 
136     private void deepBindings() {
137         int result;
138         try {
139             result = ExtentLocal.where(sl2, 42).where(sl1, 99).call(() ->
140                     deepBindings(scopeLocals.length - 1));
141         } catch (Exception e) {
142             throw new RuntimeException(e);
143         }
144         assertEquals(result, 423693);
145     }
146 
147     private int deepBindings2(int depth) throws Exception {
148         if (depth > 0) {
149             try (var unused = scopeLocals[depth].bind(depth)) {
150                 try (var structuredTaskScope = new StructuredTaskScope<Integer>(null, factory.get())) {
151                     var future = structuredTaskScope.fork(
152                             () -> ExtentLocal.where(sl1, sl1.get() + 1)
153                                     .where(scopeLocals[depth], scopeLocals[depth].get() * 2)
154                                     .call(() -> scopeLocals[depth].get() + deepBindings2(depth - 1) + sl1.get()));
155                     structuredTaskScope.join();
156                     return future.get();
157                 }
158             }
159         } else {
160             return sl2.get();
161         }
162     }
163 
164     // Serious abuse of ExtentLocals. Make sure everything still works,
165     // even with a ridiculous number of bindings.
166     @Test
167     public void manyExtentLocals() {
168         ExtentLocal<Object>[] scopeLocals = new ExtentLocal[10_000];
169         ExtentLocal.Binder[] binders = new ExtentLocal.Binder[scopeLocals.length];
170 
171         for (int i = 0; i < scopeLocals.length; i++) {
172             scopeLocals[i] = ExtentLocal.newInstance();
173             binders[i] = scopeLocals[i].bind(i);
174         }
175         long n = 0;
176         for (var sl : scopeLocals) {
177             n += (Integer)sl.get();
178         }
179         for (int i = scopeLocals.length - 1; i >= 0; --i) {
180             binders[i].close();
181         }
182         assertEquals(n, 49995000);
183         for (int i = 0; i < scopeLocals.length; i++) {
184             binders[i] = scopeLocals[i].bind(i);
185         }
186         int caught = 0;
187         // Trigger StructureViolationExceptions
188         for (int i = scopeLocals.length - 2; i >= 0; i -= 2) {
189             try {
190                 binders[i].close();
191             } catch (StructureViolationException x) {
192                 caught++;
193             }
194         }
195 
196         assertEquals(caught, 5000);
197 
198         // They should all be closed now
199         caught = 0;
200         for (int i = scopeLocals.length - 1; i >= 0; --i) {
201             binders[i].close();
202             try {
203                 binders[i].close();
204             } catch (StructureViolationException x) {
205                 caught++;
206             }
207         }
208         assertEquals(caught, 0);
209     }
210 
211     private void testDeepBindings(ExtentLocal.Carrier factoryCarrier) {
212         int val = 0;
213         try (var unused = factoryCarrier.where(sl2, 42).where(sl1, 99).bind()) {
214             val = deepBindings2(scopeLocals.length - 1);
215         } catch (Exception e) {
216             throw new RuntimeException(e);
217         }
218         assertEquals(val, 423693);
219     }
220 
221     // Make sure that stack overflows are handled correctly.
222     // Run for a while to trigger JIT compilation.
223     @Test
224     public void stackOverflowTest() {
225         assertFalse(sl2.isBound());
226         for (int i = 0; i < 200; i++) {
227             try {
228                 stackOverflow();
229             } catch (Throwable t) {
230                 ;
231             }
232             assertFalse(sl2.isBound());
233         }
234     }
235 
236     @Test
237     public void platformFactorydeepBindings() {
238         testDeepBindings(platformFactoryCarrier);
239     }
240 
241     @Test
242     public void virtualFactorydeepBindings() {
243         testDeepBindings(virtualFactoryCarrier);
244     }
245 
246     void run() {
247         manyExtentLocals();
248         platformFactorydeepBindings();
249         stackOverflowTest();
250         virtualFactorydeepBindings();
251     }
252 
253     public static void main(String[] args) {
254         new Stress().run();
255     }
256 }