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 }