1 /*
  2  * Copyright (c) 2021, 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 Test ThreadFlock with extent locals
 27  * @modules java.base/jdk.internal.misc
 28  * @modules jdk.incubator.concurrent
 29  * @compile --enable-preview -source ${jdk.version} ExtentLocalsTest.java
 30  * @run testng/othervm --enable-preview ExtentLocalsTest
 31  */
 32 
 33 import java.util.*;
 34 import java.util.concurrent.*;
 35 import java.util.concurrent.atomic.AtomicReference;
 36 import jdk.internal.misc.ThreadFlock;
 37 import jdk.incubator.concurrent.ExtentLocal;
 38 import jdk.incubator.concurrent.StructureViolationException;
 39 
 40 import org.testng.annotations.DataProvider;
 41 import org.testng.annotations.Test;
 42 import static org.testng.Assert.*;
 43 
 44 public class ExtentLocalsTest {
 45 
 46     @DataProvider(name = "factories")
 47     public Object[][] factories() {
 48         var defaultThreadFactory = Executors.defaultThreadFactory();
 49         var virtualThreadFactory = Thread.ofVirtual().factory();
 50         return new Object[][]{
 51                 { defaultThreadFactory, },
 52                 { virtualThreadFactory, },
 53         };
 54     }
 55 
 56     /**
 57      * Test inheritance of extent-local bindings.
 58      */
 59     @Test(dataProvider = "factories")
 60     public void testInheritsExtentLocals(ThreadFactory factory) throws Exception {
 61         ExtentLocal<String> NAME = ExtentLocal.newInstance();
 62         String value = ExtentLocal.where(NAME, "fred").call(() -> {
 63             var result = new AtomicReference<String>();
 64             try (var flock = ThreadFlock.open(null)) {
 65                 Thread thread = factory.newThread(() -> {
 66                     // child
 67                     result.set(NAME.get());
 68                 });
 69                 flock.start(thread);
 70             }
 71             return result.get();
 72         });
 73         assertEquals(value, "fred");
 74     }
 75 
 76     /**
 77      * Test exiting a extent local operation should close nested thread flocks.
 78      */
 79     @Test
 80     public void testStructureViolation1() {
 81         ExtentLocal<String> name = ExtentLocal.newInstance();
 82         class Box {
 83             ThreadFlock flock1;
 84             ThreadFlock flock2;
 85         }
 86         var box = new Box();
 87         try {
 88             ExtentLocal.where(name, "x1").run(() -> {
 89                 box.flock1 = ThreadFlock.open(null);
 90                 box.flock2 = ThreadFlock.open(null);
 91             });
 92             fail();
 93         } catch (StructureViolationException expected) { }
 94         assertTrue(box.flock1.isClosed());
 95         assertTrue(box.flock2.isClosed());
 96     }
 97 
 98     /**
 99      * Test closing a thread flock with enclosing extent local operations and
100      * thread flocks. This test closes enclosing flock1.
101      */
102     @Test
103     public void testStructureViolation2() {
104         ExtentLocal<String> name = ExtentLocal.newInstance();
105         try (var flock1 = ThreadFlock.open("flock1")) {
106             ExtentLocal.where(name, "x1").run(() -> {
107                 try (var flock2 = ThreadFlock.open("flock2")) {
108                     ExtentLocal.where(name, "x2").run(() -> {
109                         try (var flock3 = ThreadFlock.open("flock3")) {
110                             ExtentLocal.where(name, "x3").run(() -> {
111                                 var flock4 = ThreadFlock.open("flock4");
112 
113                                 try {
114                                     flock1.close();
115                                     fail();
116                                 } catch (StructureViolationException expected) { }
117 
118                                 assertTrue(flock1.isClosed());
119                                 assertTrue(flock2.isClosed());
120                                 assertTrue(flock3.isClosed());
121                                 assertTrue(flock4.isClosed());
122 
123                             });
124                         }
125                     });
126                 }
127             });
128         }
129     }
130 
131     /**
132      * Test closing a thread flock with enclosing extent local operations and
133      * thread flocks. This test closes enclosing flock2.
134      */
135     @Test
136     public void testStructureViolation3() {
137         ExtentLocal<String> name = ExtentLocal.newInstance();
138         try (var flock1 = ThreadFlock.open("flock1")) {
139             ExtentLocal.where(name, "x1").run(() -> {
140                 try (var flock2 = ThreadFlock.open("flock2")) {
141                     ExtentLocal.where(name, "x2").run(() -> {
142                         try (var flock3 = ThreadFlock.open("flock3")) {
143                             ExtentLocal.where(name, "x3").run(() -> {
144                                 var flock4 = ThreadFlock.open("flock4");
145 
146                                 try {
147                                     flock2.close();
148                                     fail();
149                                 } catch (StructureViolationException expected) { }
150 
151                                 assertFalse(flock1.isClosed());
152                                 assertTrue(flock2.isClosed());
153                                 assertTrue(flock3.isClosed());
154                                 assertTrue(flock4.isClosed());
155                             });
156                         }
157                     });
158                 }
159             });
160         }
161     }
162 
163     /**
164      * Test closing a thread flock with enclosing extent local operations and
165      * thread flocks. This test closes enclosing flock3.
166      */
167     @Test
168     public void testStructureViolation4() {
169         ExtentLocal<String> name = ExtentLocal.newInstance();
170         try (var flock1 = ThreadFlock.open("flock1")) {
171             ExtentLocal.where(name, "x1").run(() -> {
172                 try (var flock2 = ThreadFlock.open("flock2")) {
173                     ExtentLocal.where(name, "x2").run(() -> {
174                         try (var flock3 = ThreadFlock.open("flock3")) {
175                             ExtentLocal.where(name, "x3").run(() -> {
176                                 var flock4 = ThreadFlock.open("flock4");
177 
178                                 try {
179                                     flock3.close();
180                                     fail();
181                                 } catch (StructureViolationException expected) { }
182 
183                                 assertFalse(flock1.isClosed());
184                                 assertFalse(flock2.isClosed());
185                                 assertTrue(flock3.isClosed());
186                                 assertTrue(flock4.isClosed());
187                             });
188                         }
189                     });
190                 }
191             });
192         }
193     }
194 
195     /**
196      * Test that start throws StructureViolationException if extent-local bindings
197      * have changed.
198      */
199     @Test(dataProvider = "factories")
200     public void testStructureViolation5(ThreadFactory factory) throws Exception {
201         ExtentLocal<String> NAME = ExtentLocal.newInstance();
202         try (var flock = ThreadFlock.open(null)) {
203             ExtentLocal.where(NAME, "fred").run(() -> {
204                 Thread thread = factory.newThread(() -> { });
205                 expectThrows(StructureViolationException.class, () -> flock.start(thread));
206             });
207         }
208     }
209 }