< prev index next >

test/jdk/java/util/concurrent/StructuredTaskScope/WithScopedValue.java

Print this page

  1 /*
  2  * Copyright (c) 2022, 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  * @bug 8284199 8296779 8306647
 27  * @summary Basic tests for StructuredTaskScope with scoped values
 28  * @enablePreview
 29  * @run junit WithScopedValue
 30  */
 31 
 32 import java.util.concurrent.StructuredTaskScope;
 33 import java.util.concurrent.StructuredTaskScope.Subtask;

 34 import java.util.concurrent.StructureViolationException;
 35 import java.util.concurrent.ThreadFactory;
 36 import java.util.concurrent.atomic.AtomicBoolean;
 37 import java.util.stream.Stream;
 38 
 39 import org.junit.jupiter.api.Test;
 40 import org.junit.jupiter.params.ParameterizedTest;
 41 import org.junit.jupiter.params.provider.MethodSource;
 42 import static org.junit.jupiter.api.Assertions.*;
 43 
 44 class WithScopedValue {
 45 
 46     private static Stream<ThreadFactory> factories() {
 47         return Stream.of(Thread.ofPlatform().factory(), Thread.ofVirtual().factory());
 48     }
 49 
 50     /**
 51      * Test that fork inherits a scoped value into a child thread.
 52      */
 53     @ParameterizedTest
 54     @MethodSource("factories")
 55     void testForkInheritsScopedValue1(ThreadFactory factory) throws Exception {
 56         ScopedValue<String> name = ScopedValue.newInstance();
 57         String value = ScopedValue.callWhere(name, "x", () -> {
 58             try (var scope = new StructuredTaskScope<String>(null, factory)) {

 59                 Subtask<String> subtask = scope.fork(() -> {
 60                     return name.get(); // child should read "x"
 61                 });
 62                 scope.join();
 63                 return subtask.get();
 64             }
 65         });
 66         assertEquals(value, "x");
 67     }
 68 
 69     /**
 70      * Test that fork inherits a scoped value into a grandchild thread.
 71      */
 72     @ParameterizedTest
 73     @MethodSource("factories")
 74     void testForkInheritsScopedValue2(ThreadFactory factory) throws Exception {
 75         ScopedValue<String> name = ScopedValue.newInstance();
 76         String value = ScopedValue.callWhere(name, "x", () -> {
 77             try (var scope1 = new StructuredTaskScope<String>(null, factory)) {

 78                 Subtask<String> subtask1 = scope1.fork(() -> {
 79                     try (var scope2 = new StructuredTaskScope<String>(null, factory)) {

 80                         Subtask<String> subtask2 = scope2.fork(() -> {
 81                             return name.get(); // grandchild should read "x"
 82                         });
 83                         scope2.join();
 84                         return subtask2.get();
 85                     }
 86                 });
 87                 scope1.join();
 88                 return subtask1.get();
 89             }
 90         });
 91         assertEquals(value, "x");
 92     }
 93 
 94     /**
 95      * Test that fork inherits a rebound scoped value into a grandchild thread.
 96      */
 97     @ParameterizedTest
 98     @MethodSource("factories")
 99     void testForkInheritsScopedValue3(ThreadFactory factory) throws Exception {
100         ScopedValue<String> name = ScopedValue.newInstance();
101         String value = ScopedValue.callWhere(name, "x", () -> {
102             try (var scope1 = new StructuredTaskScope<String>(null, factory)) {

103                 Subtask<String> subtask1 = scope1.fork(() -> {
104                     assertEquals(name.get(), "x");  // child should read "x"
105 
106                     // rebind name to "y"
107                     String grandchildValue = ScopedValue.callWhere(name, "y", () -> {
108                         try (var scope2 = new StructuredTaskScope<String>(null, factory)) {

109                             Subtask<String> subtask2 = scope2.fork(() -> {
110                                 return name.get(); // grandchild should read "y"
111                             });
112                             scope2.join();
113                             return subtask2.get();
114                         }
115                     });
116 
117                     assertEquals(name.get(), "x");  // child should read "x"
118                     return grandchildValue;
119                 });
120                 scope1.join();
121                 return subtask1.get();
122             }
123         });
124         assertEquals(value, "y");
125     }
126 
127     /**
128      * Test exiting a dynamic scope with an open task scope.
129      */
130     @Test
131     void testStructureViolation1() throws Exception {
132         ScopedValue<String> name = ScopedValue.newInstance();
133         class Box {
134             StructuredTaskScope<Object> scope;
135         }
136         var box = new Box();
137         try {
138             try {
139                 ScopedValue.runWhere(name, "x", () -> {
140                     box.scope = new StructuredTaskScope<Object>();
141                 });
142                 fail();
143             } catch (StructureViolationException expected) { }
144 
145             // underlying flock should be closed and fork should fail to start a thread
146             StructuredTaskScope<Object> scope = box.scope;
147             AtomicBoolean ran = new AtomicBoolean();
148             Subtask<Object> subtask = scope.fork(() -> {
149                 ran.set(true);
150                 return null;
151             });
152             scope.join();
153             assertEquals(Subtask.State.UNAVAILABLE, subtask.state());
154             assertFalse(ran.get());
155         } finally {
156             StructuredTaskScope<Object> scope = box.scope;
157             if (scope != null) {
158                 scope.close();
159             }
160         }
161     }
162 
163     /**
164      * Test closing a StructuredTaskScope while executing in a dynamic scope.
165      */
166     @Test
167     void testStructureViolation2() throws Exception {
168         ScopedValue<String> name = ScopedValue.newInstance();
169         try (var scope = new StructuredTaskScope<String>()) {
170             ScopedValue.runWhere(name, "x", () -> {
171                 assertThrows(StructureViolationException.class, scope::close);
172             });
173         }
174     }
175 
176     /**
177      * Test fork when a scoped value is bound after a StructuredTaskScope is created.
178      */
179     @Test
180     void testStructureViolation3() throws Exception {
181         ScopedValue<String> name = ScopedValue.newInstance();
182         try (var scope = new StructuredTaskScope<String>()) {
183             ScopedValue.runWhere(name, "x", () -> {
184                 assertThrows(StructureViolationException.class,
185                         () -> scope.fork(() -> "foo"));
186             });
187         }
188     }
189 
190     /**
191      * Test fork when a scoped value is re-bound after a StructuredTaskScope is created.
192      */
193     @Test
194     void testStructureViolation4() throws Exception {
195         ScopedValue<String> name1 = ScopedValue.newInstance();
196         ScopedValue<String> name2 = ScopedValue.newInstance();
197 
198         // rebind
199         ScopedValue.runWhere(name1, "x", () -> {
200             try (var scope = new StructuredTaskScope<String>()) {
201                 ScopedValue.runWhere(name1, "y", () -> {
202                     assertThrows(StructureViolationException.class,
203                             () -> scope.fork(() -> "foo"));
204                 });
205             }
206         });
207 
208         // new binding
209         ScopedValue.runWhere(name1, "x", () -> {
210             try (var scope = new StructuredTaskScope<String>()) {
211                 ScopedValue.runWhere(name2, "y", () -> {
212                     assertThrows(StructureViolationException.class,
213                             () -> scope.fork(() -> "foo"));
214                 });
215             }
216         });
217     }
218 }

  1 /*
  2  * Copyright (c) 2022, 2024, 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  * @bug 8284199 8296779 8306647
 27  * @summary Basic tests for StructuredTaskScope with scoped values
 28  * @enablePreview
 29  * @run junit WithScopedValue
 30  */
 31 
 32 import java.util.concurrent.StructuredTaskScope;
 33 import java.util.concurrent.StructuredTaskScope.Subtask;
 34 import java.util.concurrent.StructuredTaskScope.Joiner;
 35 import java.util.concurrent.StructureViolationException;
 36 import java.util.concurrent.ThreadFactory;
 37 import java.util.concurrent.atomic.AtomicBoolean;
 38 import java.util.stream.Stream;
 39 
 40 import org.junit.jupiter.api.Test;
 41 import org.junit.jupiter.params.ParameterizedTest;
 42 import org.junit.jupiter.params.provider.MethodSource;
 43 import static org.junit.jupiter.api.Assertions.*;
 44 
 45 class WithScopedValue {
 46 
 47     private static Stream<ThreadFactory> factories() {
 48         return Stream.of(Thread.ofPlatform().factory(), Thread.ofVirtual().factory());
 49     }
 50 
 51     /**
 52      * Test that fork inherits a scoped value into a child thread.
 53      */
 54     @ParameterizedTest
 55     @MethodSource("factories")
 56     void testForkInheritsScopedValue1(ThreadFactory factory) throws Exception {
 57         ScopedValue<String> name = ScopedValue.newInstance();
 58         String value = ScopedValue.callWhere(name, "x", () -> {
 59             try (var scope = StructuredTaskScope.open(Joiner.awaitAll(),
 60                                                       cf -> cf.withThreadFactory(factory))) {
 61                 Subtask<String> subtask = scope.fork(() -> {
 62                     return name.get(); // child should read "x"
 63                 });
 64                 scope.join();
 65                 return subtask.get();
 66             }
 67         });
 68         assertEquals(value, "x");
 69     }
 70 
 71     /**
 72      * Test that fork inherits a scoped value into a grandchild thread.
 73      */
 74     @ParameterizedTest
 75     @MethodSource("factories")
 76     void testForkInheritsScopedValue2(ThreadFactory factory) throws Exception {
 77         ScopedValue<String> name = ScopedValue.newInstance();
 78         String value = ScopedValue.callWhere(name, "x", () -> {
 79             try (var scope1 = StructuredTaskScope.open(Joiner.awaitAll(),
 80                                                        cf -> cf.withThreadFactory(factory))) {
 81                 Subtask<String> subtask1 = scope1.fork(() -> {
 82                     try (var scope2 = StructuredTaskScope.open(Joiner.awaitAll(),
 83                                                                cf -> cf.withThreadFactory(factory))) {
 84                         Subtask<String> subtask2 = scope2.fork(() -> {
 85                             return name.get(); // grandchild should read "x"
 86                         });
 87                         scope2.join();
 88                         return subtask2.get();
 89                     }
 90                 });
 91                 scope1.join();
 92                 return subtask1.get();
 93             }
 94         });
 95         assertEquals(value, "x");
 96     }
 97 
 98     /**
 99      * Test that fork inherits a rebound scoped value into a grandchild thread.
100      */
101     @ParameterizedTest
102     @MethodSource("factories")
103     void testForkInheritsScopedValue3(ThreadFactory factory) throws Exception {
104         ScopedValue<String> name = ScopedValue.newInstance();
105         String value = ScopedValue.callWhere(name, "x", () -> {
106             try (var scope1 = StructuredTaskScope.open(Joiner.awaitAll(),
107                                                        cf -> cf.withThreadFactory(factory))) {
108                 Subtask<String> subtask1 = scope1.fork(() -> {
109                     assertEquals(name.get(), "x");  // child should read "x"
110 
111                     // rebind name to "y"
112                     String grandchildValue = ScopedValue.callWhere(name, "y", () -> {
113                         try (var scope2 = StructuredTaskScope.open(Joiner.awaitAll(),
114                                                                    cf -> cf.withThreadFactory(factory))) {
115                             Subtask<String> subtask2 = scope2.fork(() -> {
116                                 return name.get(); // grandchild should read "y"
117                             });
118                             scope2.join();
119                             return subtask2.get();
120                         }
121                     });
122 
123                     assertEquals(name.get(), "x");  // child should read "x"
124                     return grandchildValue;
125                 });
126                 scope1.join();
127                 return subtask1.get();
128             }
129         });
130         assertEquals(value, "y");
131     }
132 
133     /**
134      * Test exiting a dynamic scope with an open task scope.
135      */
136     @Test
137     void testStructureViolation1() throws Exception {
138         ScopedValue<String> name = ScopedValue.newInstance();
139         class Box {
140             StructuredTaskScope<Object, Void> scope;
141         }
142         var box = new Box();
143         try {
144             try {
145                 ScopedValue.runWhere(name, "x", () -> {
146                     box.scope = StructuredTaskScope.open(Joiner.awaitAll());
147                 });
148                 fail();
149             } catch (StructureViolationException expected) { }
150 
151             // underlying flock should be closed and fork should fail to start a thread
152             StructuredTaskScope<Object, Void> scope = box.scope;
153             AtomicBoolean ran = new AtomicBoolean();
154             Subtask<Object> subtask = scope.fork(() -> {
155                 ran.set(true);
156                 return null;
157             });
158             scope.join();
159             assertEquals(Subtask.State.UNAVAILABLE, subtask.state());
160             assertFalse(ran.get());
161         } finally {
162             StructuredTaskScope<Object, Void> scope = box.scope;
163             if (scope != null) {
164                 scope.close();
165             }
166         }
167     }
168 
169     /**
170      * Test closing a StructuredTaskScope while executing in a dynamic scope.
171      */
172     @Test
173     void testStructureViolation2() throws Exception {
174         ScopedValue<String> name = ScopedValue.newInstance();
175         try (var scope = StructuredTaskScope.open(Joiner.awaitAll())) {
176             ScopedValue.runWhere(name, "x", () -> {
177                 assertThrows(StructureViolationException.class, scope::close);
178             });
179         }
180     }
181 
182     /**
183      * Test fork when a scoped value is bound after a StructuredTaskScope is created.
184      */
185     @Test
186     void testStructureViolation3() throws Exception {
187         ScopedValue<String> name = ScopedValue.newInstance();
188         try (var scope = StructuredTaskScope.open(Joiner.awaitAll())) {
189             ScopedValue.runWhere(name, "x", () -> {
190                 assertThrows(StructureViolationException.class,
191                         () -> scope.fork(() -> "foo"));
192             });
193         }
194     }
195 
196     /**
197      * Test fork when a scoped value is re-bound after a StructuredTaskScope is created.
198      */
199     @Test
200     void testStructureViolation4() throws Exception {
201         ScopedValue<String> name1 = ScopedValue.newInstance();
202         ScopedValue<String> name2 = ScopedValue.newInstance();
203 
204         // rebind
205         ScopedValue.runWhere(name1, "x", () -> {
206             try (var scope = StructuredTaskScope.open(Joiner.awaitAll())) {
207                 ScopedValue.runWhere(name1, "y", () -> {
208                     assertThrows(StructureViolationException.class,
209                             () -> scope.fork(() -> "foo"));
210                 });
211             }
212         });
213 
214         // new binding
215         ScopedValue.runWhere(name1, "x", () -> {
216             try (var scope = StructuredTaskScope.open(Joiner.awaitAll())) {
217                 ScopedValue.runWhere(name2, "y", () -> {
218                     assertThrows(StructureViolationException.class,
219                             () -> scope.fork(() -> "foo"));
220                 });
221             }
222         });
223     }
224 }
< prev index next >