1 /*
  2  * Copyright (c) 2022, 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 Basic tests for StructuredTaskScope with extent-locals
 27  * @enablePreview
 28  * @modules jdk.incubator.concurrent
 29  * @run testng/othervm ExtentLocalsTest
 30  */
 31 
 32 import jdk.incubator.concurrent.ExtentLocal;
 33 import jdk.incubator.concurrent.StructuredTaskScope;
 34 import jdk.incubator.concurrent.StructureViolationException;
 35 import java.util.concurrent.Future;
 36 import java.util.concurrent.atomic.AtomicBoolean;
 37 import org.testng.annotations.Test;
 38 import static org.testng.Assert.*;
 39 
 40 public class ExtentLocalsTest {
 41 
 42     /**
 43      * Test that fork inherits extent-local bindings.
 44      */
 45     @Test
 46     public void testForkInheritsExtentLocals1() throws Exception {
 47         ExtentLocal<String> NAME = ExtentLocal.newInstance();
 48         String value = ExtentLocal.where(NAME, "x").call(() -> {
 49             try (var scope = new StructuredTaskScope()) {
 50                 Future<String> future = scope.fork(() -> {
 51                     // child
 52                     return NAME.get();
 53                 });
 54                 scope.join();
 55                 return future.resultNow();
 56             }
 57         });
 58         assertEquals(value, "x");
 59     }
 60 
 61     /**
 62      * Test that fork inherits extent-local bindings into a grandchild.
 63      */
 64     @Test
 65     public void testForkInheritsExtentLocals2() throws Exception {
 66         ExtentLocal<String> NAME = ExtentLocal.newInstance();
 67         String value = ExtentLocal.where(NAME, "x").call(() -> {
 68             try (var scope1 = new StructuredTaskScope()) {
 69                 Future<String> future1 = scope1.fork(() -> {
 70                     try (var scope2 = new StructuredTaskScope()) {
 71                         Future<String> future2 = scope2.fork(() -> {
 72                             // grandchild
 73                             return NAME.get();
 74                         });
 75                         scope2.join();
 76                         return future2.resultNow();
 77                     }
 78                 });
 79                 scope1.join();
 80                 return future1.resultNow();
 81             }
 82         });
 83         assertEquals(value, "x");
 84     }
 85 
 86     /**
 87      * Test exiting an extent local operation closes the thread flock of a nested scope
 88      * and throws StructureViolationException.
 89      */
 90     @Test
 91     public void testStructureViolation1() throws Exception {
 92         ExtentLocal<String> name = ExtentLocal.newInstance();
 93         class Box {
 94             StructuredTaskScope<Object> scope;
 95         }
 96         var box = new Box();
 97         try {
 98             try {
 99                 ExtentLocal.where(name, "x").run(() -> {
100                     box.scope = new StructuredTaskScope();
101                 });
102                 fail();
103             } catch (StructureViolationException expected) { }
104 
105             // underlying flock should be closed, fork should return a cancelled task
106             StructuredTaskScope<Object> scope = box.scope;
107             AtomicBoolean ran = new AtomicBoolean();
108             Future<String> future = scope.fork(() -> {
109                 ran.set(true);
110                 return null;
111             });
112             assertTrue(future.isCancelled());
113             scope.join();
114             assertFalse(ran.get());
115 
116         } finally {
117             StructuredTaskScope<Object> scope = box.scope;
118             if (scope != null) {
119                 scope.close();
120             }
121         }
122     }
123 
124     /**
125      * Test that fork throws StructureViolationException if extent-local bindings
126      * created after StructuredTaskScope is created.
127      */
128     @Test
129     public void testStructureViolation2() throws Exception {
130         ExtentLocal<String> NAME = ExtentLocal.newInstance();
131 
132         try (var scope = new StructuredTaskScope()) {
133             ExtentLocal.where(NAME, "x").run(() -> {
134                 assertThrows(StructureViolationException.class,
135                         () -> scope.fork(() -> "foo"));
136             });
137         }
138     }
139 
140     /**
141      * Test that fork throws StructureViolationException if extent-local bindings
142      * change after StructuredTaskScope is created.
143      */
144     @Test
145     public void testStructureViolation3() throws Exception {
146         ExtentLocal<String> NAME1 = ExtentLocal.newInstance();
147         ExtentLocal<String> NAME2 = ExtentLocal.newInstance();
148 
149         // re-bind
150         ExtentLocal.where(NAME1, "x").run(() -> {
151             try (var scope = new StructuredTaskScope()) {
152                 ExtentLocal.where(NAME1, "y").run(() -> {
153                     assertThrows(StructureViolationException.class,
154                             () -> scope.fork(() -> "foo"));
155                 });
156             }
157         });
158 
159         // new binding
160         ExtentLocal.where(NAME1, "x").run(() -> {
161             try (var scope = new StructuredTaskScope()) {
162                 ExtentLocal.where(NAME2, "y").run(() -> {
163                     assertThrows(StructureViolationException.class,
164                             () -> scope.fork(() -> "foo"));
165                 });
166             }
167         });
168     }
169 }