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 StressStackOverflow the recovery path for ScopedValue
27 * @modules jdk.incubator.concurrent
28 * @compile StressStackOverflow.java
29 * @run main/othervm/timeout=300 -XX:-TieredCompilation StressStackOverflow
30 * @run main/othervm/timeout=300 -XX:TieredStopAtLevel=1 StressStackOverflow
31 * @run main/othervm/timeout=300 StressStackOverflow
32 */
33
34 import java.util.concurrent.Callable;
35 import java.util.concurrent.ThreadFactory;
36 import java.util.concurrent.ThreadLocalRandom;
37 import jdk.incubator.concurrent.ScopedValue;
38 import jdk.incubator.concurrent.StructureViolationException;
39 import jdk.incubator.concurrent.StructuredTaskScope;
40
41 public class StressStackOverflow {
42 public static final ScopedValue<Integer> el = ScopedValue.newInstance();
43
44 public static final ScopedValue<Integer> inheritedValue = ScopedValue.newInstance();
45
46 final ThreadLocalRandom tlr = ThreadLocalRandom.current();
47 static final TestFailureException testFailureException = new TestFailureException("Unexpected value for ScopedValue");
48 int ITERS = 1_000_000;
49
50 static class TestFailureException extends RuntimeException {
51 TestFailureException(String s) { super(s); }
52 }
53
54 // Test the ScopedValue recovery mechanism for stack overflows. We implement both Callable
55 // and Runnable interfaces. Which one gets tested depends on the constructor argument.
56 class DeepRecursion implements Callable, Runnable {
57
58 static enum Behaviour {CALL, RUN}
59 final Behaviour behaviour;
60
61 public DeepRecursion(Behaviour behaviour) {
62 this.behaviour = behaviour;
63 }
64
65 public void run() {
66 final var last = el.get();
67 ITERS--;
68 var nextRandomFloat = tlr.nextFloat();
69 try {
70 switch (behaviour) {
71 case CALL ->
72 ScopedValue.where(el, el.get() + 1).call(() -> fibonacci_pad(20, this));
73 case RUN ->
74 ScopedValue.where(el, el.get() + 1).run(() -> fibonacci_pad(20, this));
75 }
76 if (!last.equals(el.get())) {
77 throw testFailureException;
78 }
79 } catch (StackOverflowError e) {
80 if (nextRandomFloat <= 0.1) {
81 ScopedValue.where(el, el.get() + 1).run(this);
82 }
83 } catch (TestFailureException e) {
84 throw e;
85 } catch (Throwable throwable) {
86 // StackOverflowErrors cause many different failures. These include
87 // StructureViolationExceptions and InvocationTargetExceptions. This test
88 // checks that, no matter what the failure mode, scoped values are handled
89 // correctly.
90 } finally {
91 if (!last.equals(el.get())) {
92 throw testFailureException;
93 }
94 }
95
96 Thread.yield();
97 }
98
99 public Object call() {
100 run();
101 return null;
102 }
103 }
104
105 static final Runnable nop = new Runnable() {
106 public void run() { }
107 };
108
109 // Consume some stack.
110 //
111
112 // The double recursion used here prevents an optimizing JIT from
113 // inlining all the recursive calls, which would make it
114 // ineffective.
115 private long fibonacci_pad1(int n, Runnable op) {
116 if (n <= 1) {
117 op.run();
118 return n;
119 }
120 return fibonacci_pad1(n - 1, op) + fibonacci_pad1(n - 2, nop);
121 }
122
125 long fibonacci_pad(int n, Runnable op) {
126 final var last = el.get();
127 try {
128 return fibonacci_pad1(tlr.nextInt(n), op);
129 } catch (StackOverflowError err) {
130 if (!inheritedValue.get().equals(I_42)) {
131 throw testFailureException;
132 }
133 if (!last.equals(el.get())) {
134 throw testFailureException;
135 }
136 throw err;
137 }
138 }
139
140 // Run op in a new thread. Platform or virtual threads are chosen at random.
141 void runInNewThread(Runnable op) {
142 var threadFactory
143 = (tlr.nextBoolean() ? Thread.ofPlatform() : Thread.ofVirtual()).factory();
144 try (var scope = new StructuredTaskScope<Object>("", threadFactory)) {
145 var future = scope.fork(() -> {
146 op.run();
147 return null;
148 });
149 future.get();
150 scope.join();
151 } catch (Exception e) {
152 throw new RuntimeException(e);
153 }
154 }
155
156 public void run() {
157 try {
158 ScopedValue.where(inheritedValue, 42).where(el, 0).run(() -> {
159 try (var scope = new StructuredTaskScope<Object>()) {
160 try {
161 if (tlr.nextBoolean()) {
162 // Repeatedly test Scoped Values set by ScopedValue::call() and ScopedValue::run()
163 final var deepRecursion
164 = new DeepRecursion(tlr.nextBoolean() ? DeepRecursion.Behaviour.CALL : DeepRecursion.Behaviour.RUN);
165 deepRecursion.run();
166 } else {
167 // Recursively run ourself until we get a stack overflow
168 // Catch the overflow and make sure the recovery path works
169 // for values inherited from a StructuredTaskScope.
170 Runnable op = new Runnable() {
171 public void run() {
172 try {
173 fibonacci_pad(20, this);
174 } catch (StackOverflowError e) {
175 } catch (TestFailureException e) {
176 throw e;
177 } catch (Throwable throwable) {
178 // StackOverflowErrors cause many different failures. These include
179 // StructureViolationExceptions and InvocationTargetExceptions. This test
180 // checks that, no matter what the failure mode, scoped values are handled
181 // correctly.
182 } finally {
183 if (!inheritedValue.get().equals(I_42)) {
184 throw testFailureException;
|
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 StressStackOverflow the recovery path for ScopedValue
27 * @enablePreview
28 * @run main/othervm/timeout=300 -XX:-TieredCompilation StressStackOverflow
29 * @run main/othervm/timeout=300 -XX:TieredStopAtLevel=1 StressStackOverflow
30 * @run main/othervm/timeout=300 StressStackOverflow
31 */
32
33 import java.util.concurrent.Callable;
34 import java.util.concurrent.ThreadFactory;
35 import java.util.concurrent.ThreadLocalRandom;
36 import java.util.concurrent.StructureViolationException;
37 import java.util.concurrent.StructuredTaskScope;
38 import java.util.function.Supplier;
39
40 public class StressStackOverflow {
41 public static final ScopedValue<Integer> el = ScopedValue.newInstance();
42
43 public static final ScopedValue<Integer> inheritedValue = ScopedValue.newInstance();
44
45 final ThreadLocalRandom tlr = ThreadLocalRandom.current();
46 static final TestFailureException testFailureException = new TestFailureException("Unexpected value for ScopedValue");
47 int ITERS = 1_000_000;
48
49 static class TestFailureException extends RuntimeException {
50 TestFailureException(String s) { super(s); }
51 }
52
53 // Test the ScopedValue recovery mechanism for stack overflows. We implement both Callable
54 // and Runnable interfaces. Which one gets tested depends on the constructor argument.
55 class DeepRecursion implements Callable, Supplier, Runnable {
56
57 static enum Behaviour {
58 CALL, GET, RUN;
59 private static Behaviour[] values = values();
60 public static Behaviour choose(ThreadLocalRandom tlr) {
61 return values[tlr.nextInt(3)];
62 }
63 }
64
65 final Behaviour behaviour;
66
67 public DeepRecursion(Behaviour behaviour) {
68 this.behaviour = behaviour;
69 }
70
71 public void run() {
72 final var last = el.get();
73 ITERS--;
74 var nextRandomFloat = tlr.nextFloat();
75 try {
76 switch (behaviour) {
77 case CALL ->
78 ScopedValue.where(el, el.get() + 1).call(() -> fibonacci_pad(20, this));
79 case GET ->
80 ScopedValue.where(el, el.get() + 1).get(() -> fibonacci_pad(20, this));
81 case RUN ->
82 ScopedValue.where(el, el.get() + 1).run(() -> fibonacci_pad(20, this));
83 }
84 if (!last.equals(el.get())) {
85 throw testFailureException;
86 }
87 } catch (StackOverflowError e) {
88 if (nextRandomFloat <= 0.1) {
89 ScopedValue.where(el, el.get() + 1).run(this);
90 }
91 } catch (TestFailureException e) {
92 throw e;
93 } catch (Throwable throwable) {
94 // StackOverflowErrors cause many different failures. These include
95 // StructureViolationExceptions and InvocationTargetExceptions. This test
96 // checks that, no matter what the failure mode, scoped values are handled
97 // correctly.
98 } finally {
99 if (!last.equals(el.get())) {
100 throw testFailureException;
101 }
102 }
103
104 Thread.yield();
105 }
106
107 public Object get() {
108 run();
109 return null;
110 }
111
112 public Object call() {
113 return get();
114 }
115 }
116
117 static final Runnable nop = new Runnable() {
118 public void run() { }
119 };
120
121 // Consume some stack.
122 //
123
124 // The double recursion used here prevents an optimizing JIT from
125 // inlining all the recursive calls, which would make it
126 // ineffective.
127 private long fibonacci_pad1(int n, Runnable op) {
128 if (n <= 1) {
129 op.run();
130 return n;
131 }
132 return fibonacci_pad1(n - 1, op) + fibonacci_pad1(n - 2, nop);
133 }
134
137 long fibonacci_pad(int n, Runnable op) {
138 final var last = el.get();
139 try {
140 return fibonacci_pad1(tlr.nextInt(n), op);
141 } catch (StackOverflowError err) {
142 if (!inheritedValue.get().equals(I_42)) {
143 throw testFailureException;
144 }
145 if (!last.equals(el.get())) {
146 throw testFailureException;
147 }
148 throw err;
149 }
150 }
151
152 // Run op in a new thread. Platform or virtual threads are chosen at random.
153 void runInNewThread(Runnable op) {
154 var threadFactory
155 = (tlr.nextBoolean() ? Thread.ofPlatform() : Thread.ofVirtual()).factory();
156 try (var scope = new StructuredTaskScope<Object>("", threadFactory)) {
157 var handle = scope.fork(() -> {
158 op.run();
159 return null;
160 });
161 scope.join();
162 handle.get();
163 } catch (Exception e) {
164 throw new RuntimeException(e);
165 }
166 }
167
168 public void run() {
169 try {
170 ScopedValue.where(inheritedValue, 42).where(el, 0).run(() -> {
171 try (var scope = new StructuredTaskScope<Object>()) {
172 try {
173 if (tlr.nextBoolean()) {
174 // Repeatedly test Scoped Values set by ScopedValue::call(), get(), and run()
175 final var deepRecursion
176 = new DeepRecursion(DeepRecursion.Behaviour.choose(tlr));
177 deepRecursion.run();
178 } else {
179 // Recursively run ourself until we get a stack overflow
180 // Catch the overflow and make sure the recovery path works
181 // for values inherited from a StructuredTaskScope.
182 Runnable op = new Runnable() {
183 public void run() {
184 try {
185 fibonacci_pad(20, this);
186 } catch (StackOverflowError e) {
187 } catch (TestFailureException e) {
188 throw e;
189 } catch (Throwable throwable) {
190 // StackOverflowErrors cause many different failures. These include
191 // StructureViolationExceptions and InvocationTargetExceptions. This test
192 // checks that, no matter what the failure mode, scoped values are handled
193 // correctly.
194 } finally {
195 if (!inheritedValue.get().equals(I_42)) {
196 throw testFailureException;
|