1 /* 2 * Copyright (c) 2021, 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 * @summary Test ThreadFlock with scoped values 27 * @modules java.base/jdk.internal.misc 28 * @modules jdk.incubator.concurrent 29 * @run junit WithScopedValue 30 */ 31 32 import jdk.internal.misc.ThreadFlock; 33 import jdk.incubator.concurrent.ScopedValue; 34 import jdk.incubator.concurrent.StructureViolationException; 35 import java.util.concurrent.ThreadFactory; 36 import java.util.concurrent.atomic.AtomicReference; 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 inheritance of a scoped value. 52 */ 53 @ParameterizedTest 54 @MethodSource("factories") 55 void testInheritsScopedValue(ThreadFactory factory) throws Exception { 56 ScopedValue<String> name = ScopedValue.newInstance(); 57 String value = ScopedValue.where(name, "duke", () -> { 58 var result = new AtomicReference<String>(); 59 try (var flock = ThreadFlock.open(null)) { 60 Thread thread = factory.newThread(() -> { 61 // child 62 result.set(name.get()); 63 }); 64 flock.start(thread); 65 } 66 return result.get(); 67 }); 68 assertEquals("duke", value); 69 } 70 71 /** 72 * Test exiting a dynamic scope with open thread flocks. 73 */ 74 @Test 75 void testStructureViolation1() { 76 ScopedValue<String> name = ScopedValue.newInstance(); 77 class Box { 78 ThreadFlock flock1; 79 ThreadFlock flock2; 80 } 81 var box = new Box(); 82 try { 83 ScopedValue.where(name, "x1", () -> { 84 box.flock1 = ThreadFlock.open(null); 85 box.flock2 = ThreadFlock.open(null); 86 }); 87 fail(); 88 } catch (StructureViolationException expected) { } 89 assertTrue(box.flock1.isClosed()); 90 assertTrue(box.flock2.isClosed()); 91 } 92 93 /** 94 * Test closing a thread flock while in a dynamic scope and with enclosing thread 95 * flocks. This test closes enclosing flock1. 96 */ 97 @Test 98 void testStructureViolation2() { 99 ScopedValue<String> name = ScopedValue.newInstance(); 100 try (var flock1 = ThreadFlock.open("flock1")) { 101 ScopedValue.where(name, "x1", () -> { 102 try (var flock2 = ThreadFlock.open("flock2")) { 103 ScopedValue.where(name, "x2", () -> { 104 try (var flock3 = ThreadFlock.open("flock3")) { 105 ScopedValue.where(name, "x3", () -> { 106 var flock4 = ThreadFlock.open("flock4"); 107 108 try { 109 flock1.close(); 110 fail(); 111 } catch (StructureViolationException expected) { } 112 113 assertTrue(flock1.isClosed()); 114 assertTrue(flock2.isClosed()); 115 assertTrue(flock3.isClosed()); 116 assertTrue(flock4.isClosed()); 117 }); 118 } 119 }); 120 } 121 }); 122 } 123 } 124 125 /** 126 * Test closing a thread flock while in a dynamic scope and with enclosing thread 127 * flocks. This test closes enclosing flock2. 128 */ 129 @Test 130 void testStructureViolation3() { 131 ScopedValue<String> name = ScopedValue.newInstance(); 132 try (var flock1 = ThreadFlock.open("flock1")) { 133 ScopedValue.where(name, "x1", () -> { 134 try (var flock2 = ThreadFlock.open("flock2")) { 135 ScopedValue.where(name, "x2", () -> { 136 try (var flock3 = ThreadFlock.open("flock3")) { 137 ScopedValue.where(name, "x3", () -> { 138 var flock4 = ThreadFlock.open("flock4"); 139 140 try { 141 flock2.close(); 142 fail(); 143 } catch (StructureViolationException expected) { } 144 145 assertFalse(flock1.isClosed()); 146 assertTrue(flock2.isClosed()); 147 assertTrue(flock3.isClosed()); 148 assertTrue(flock4.isClosed()); 149 }); 150 } 151 }); 152 } 153 }); 154 } 155 } 156 157 /** 158 * Test closing a thread flock while in a dynamic scope and with enclosing thread 159 * flocks. This test closes enclosing flock3. 160 */ 161 @Test 162 void testStructureViolation4() { 163 ScopedValue<String> name = ScopedValue.newInstance(); 164 try (var flock1 = ThreadFlock.open("flock1")) { 165 ScopedValue.where(name, "x1", () -> { 166 try (var flock2 = ThreadFlock.open("flock2")) { 167 ScopedValue.where(name, "x2", () -> { 168 try (var flock3 = ThreadFlock.open("flock3")) { 169 ScopedValue.where(name, "x3", () -> { 170 var flock4 = ThreadFlock.open("flock4"); 171 172 try { 173 flock3.close(); 174 fail(); 175 } catch (StructureViolationException expected) { } 176 177 assertFalse(flock1.isClosed()); 178 assertFalse(flock2.isClosed()); 179 assertTrue(flock3.isClosed()); 180 assertTrue(flock4.isClosed()); 181 }); 182 } 183 }); 184 } 185 }); 186 } 187 } 188 189 /** 190 * Test start when a scoped value is bound after a thread flock is created. 191 */ 192 @ParameterizedTest 193 @MethodSource("factories") 194 void testStructureViolation5(ThreadFactory factory) throws Exception { 195 ScopedValue<String> name = ScopedValue.newInstance(); 196 try (var flock = ThreadFlock.open(null)) { 197 ScopedValue.where(name, "duke", () -> { 198 Thread thread = factory.newThread(() -> { }); 199 assertThrows(StructureViolationException.class, () -> flock.start(thread)); 200 }); 201 } 202 } 203 204 /** 205 * Test start when a scoped value is re-bound after a thread flock is created. 206 */ 207 @ParameterizedTest 208 @MethodSource("factories") 209 void testStructureViolation6(ThreadFactory factory) throws Exception { 210 ScopedValue<String> name = ScopedValue.newInstance(); 211 ScopedValue.where(name, "duke", () -> { 212 try (var flock = ThreadFlock.open(null)) { 213 ScopedValue.where(name, "duchess", () -> { 214 Thread thread = factory.newThread(() -> { }); 215 assertThrows(StructureViolationException.class, () -> flock.start(thread)); 216 }); 217 } 218 }); 219 } 220 }