< prev index next >

test/jdk/java/foreign/TestResourceScope.java

Print this page

 22  */
 23 
 24 /*
 25  * @test
 26  * @modules java.base/jdk.internal.ref
 27  *          jdk.incubator.foreign/jdk.incubator.foreign
 28  * @run testng/othervm TestResourceScope
 29  */
 30 
 31 import java.lang.ref.Cleaner;
 32 
 33 import jdk.incubator.foreign.ResourceScope;
 34 import jdk.internal.ref.CleanerFactory;
 35 
 36 import org.testng.annotations.DataProvider;
 37 import org.testng.annotations.Test;
 38 import static org.testng.Assert.*;
 39 
 40 import java.util.ArrayList;
 41 import java.util.List;

 42 import java.util.concurrent.atomic.AtomicInteger;
 43 import java.util.concurrent.atomic.AtomicReference;
 44 import java.util.function.Supplier;
 45 import java.util.stream.IntStream;
 46 
 47 public class TestResourceScope {
 48 
 49     final static int N_THREADS = 100;
 50 
 51     @Test(dataProvider = "cleaners")
 52     public void testConfined(Supplier<Cleaner> cleanerSupplier) {
 53         AtomicInteger acc = new AtomicInteger();
 54         Cleaner cleaner = cleanerSupplier.get();
 55         ResourceScope scope = cleaner != null ?
 56                 ResourceScope.newConfinedScope(cleaner) :
 57                 ResourceScope.newConfinedScope();
 58         for (int i = 0 ; i < N_THREADS ; i++) {
 59             int delta = i;
 60             scope.addCloseAction(() -> acc.addAndGet(delta));
 61         }

140         threads.forEach(t -> {
141             try {
142                 t.join();
143             } catch (InterruptedException ex) {
144                 fail();
145             }
146         });
147 
148         if (cleaner == null) {
149             assertEquals(acc.get(), IntStream.range(0, N_THREADS).sum());
150         } else {
151             scope = null;
152             scopeRef.set(null);
153             int expected = IntStream.range(0, N_THREADS).sum();
154             while (acc.get() != expected) {
155                 kickGC();
156             }
157         }
158     }
159 
160     @Test(dataProvider = "cleaners")
161     public void testLockSingleThread(Supplier<Cleaner> cleanerSupplier) {
162         Cleaner cleaner = cleanerSupplier.get();
163         ResourceScope scope = cleaner != null ?
164                 ResourceScope.newConfinedScope(cleaner) :
165                 ResourceScope.newConfinedScope();
166         List<ResourceScope.Handle> handles = new ArrayList<>();
167         for (int i = 0 ; i < N_THREADS ; i++) {
168             handles.add(scope.acquire());


169         }
170 
171         while (true) {
172             try {
173                 scope.close();
174                 assertEquals(handles.size(), 0);
175                 break;
176             } catch (IllegalStateException ex) {
177                 assertTrue(handles.size() > 0);
178                 ResourceScope.Handle handle = handles.remove(0);
179                 scope.release(handle);
180                 scope.release(handle); // make sure it's idempotent
181                 scope.release(handle); // make sure it's idempotent
182             }
183         }
184     }
185 
186     @Test(dataProvider = "cleaners")
187     public void testLockSharedMultiThread(Supplier<Cleaner> cleanerSupplier) {
188         Cleaner cleaner = cleanerSupplier.get();
189         ResourceScope scope = cleaner != null ?
190                 ResourceScope.newSharedScope(cleaner) :
191                 ResourceScope.newSharedScope();
192         AtomicInteger lockCount = new AtomicInteger();
193         for (int i = 0 ; i < N_THREADS ; i++) {
194             new Thread(() -> {
195                 try {
196                     ResourceScope.Handle handle = scope.acquire(); // this can throw if segment has been closed
197                     lockCount.incrementAndGet();
198                     waitSomeTime();
199                     lockCount.decrementAndGet();
200                     scope.release(handle); // cannot throw (acquired segments cannot be closed)
201                     scope.release(handle); // cannot throw (idempotent)
202                     scope.release(handle); // cannot throw (idempotent)
203                 } catch (IllegalStateException ex) {
204                     // might be already closed - do nothing
205                 }
206             }).start();
207         }
208 
209         while (true) {
210             try {
211                 scope.close();
212                 assertEquals(lockCount.get(), 0);
213                 break;
214             } catch (IllegalStateException ex) {
215                 waitSomeTime();
216             }
217         }
218     }
219 
220     @Test
221     public void testCloseEmptyConfinedScope() {
222         ResourceScope.newConfinedScope().close();
223     }
224 
225     @Test
226     public void testCloseEmptySharedScope() {
227         ResourceScope.newSharedScope().close();
228     }
229 
230     @Test
231     public void testCloseConfinedLock() {
232         ResourceScope scope = ResourceScope.newConfinedScope();
233         ResourceScope.Handle handle = scope.acquire();

234         AtomicReference<Throwable> failure = new AtomicReference<>();
235         Thread t = new Thread(() -> {
236             try {
237                 scope.release(handle);
238                 scope.release(handle); // make sure it's idempotent
239                 scope.release(handle); // make sure it's idempotent
240             } catch (Throwable ex) {
241                 failure.set(ex);
242             }
243         });
244         t.start();
245         try {
246             t.join();
247             assertNotNull(failure.get());
248             assertEquals(failure.get().getClass(), IllegalStateException.class);
249         } catch (Throwable ex) {
250             throw new AssertionError(ex);
251         }
252     }
253 
254     @Test(dataProvider = "scopes")
255     public void testScopeHandles(Supplier<ResourceScope> scopeFactory) {
256         ResourceScope scope = scopeFactory.get();
257         acquireRecursive(scope, 5);
258         if (!scope.isImplicit()) {
259             scope.close();
260         }
261     }
262 






263     private void acquireRecursive(ResourceScope scope, int acquireCount) {
264         ResourceScope.Handle handle = scope.acquire();
265         assertEquals(handle.scope(), scope);
266         if (acquireCount > 0) {
267             // recursive acquire
268             acquireRecursive(scope, acquireCount - 1);




269         }
270         if (!scope.isImplicit()) {
271             assertThrows(IllegalStateException.class, scope::close);






272         }
273         scope.release(handle);
274         scope.release(handle); // make sure it's idempotent
275         scope.release(handle); // make sure it's idempotent













































276     }
277 
278     private void waitSomeTime() {
279         try {
280             Thread.sleep(10);
281         } catch (InterruptedException ex) {
282             // ignore
283         }
284     }
285 
286     private void kickGC() {
287         for (int i = 0 ; i < 100 ; i++) {
288             byte[] b = new byte[100];
289             System.gc();
290             Thread.onSpinWait();
291         }
292     }
293 
294     @DataProvider
295     static Object[][] cleaners() {

 22  */
 23 
 24 /*
 25  * @test
 26  * @modules java.base/jdk.internal.ref
 27  *          jdk.incubator.foreign/jdk.incubator.foreign
 28  * @run testng/othervm TestResourceScope
 29  */
 30 
 31 import java.lang.ref.Cleaner;
 32 
 33 import jdk.incubator.foreign.ResourceScope;
 34 import jdk.internal.ref.CleanerFactory;
 35 
 36 import org.testng.annotations.DataProvider;
 37 import org.testng.annotations.Test;
 38 import static org.testng.Assert.*;
 39 
 40 import java.util.ArrayList;
 41 import java.util.List;
 42 import java.util.Set;
 43 import java.util.concurrent.atomic.AtomicInteger;
 44 import java.util.concurrent.atomic.AtomicReference;
 45 import java.util.function.Supplier;
 46 import java.util.stream.IntStream;
 47 
 48 public class TestResourceScope {
 49 
 50     final static int N_THREADS = 100;
 51 
 52     @Test(dataProvider = "cleaners")
 53     public void testConfined(Supplier<Cleaner> cleanerSupplier) {
 54         AtomicInteger acc = new AtomicInteger();
 55         Cleaner cleaner = cleanerSupplier.get();
 56         ResourceScope scope = cleaner != null ?
 57                 ResourceScope.newConfinedScope(cleaner) :
 58                 ResourceScope.newConfinedScope();
 59         for (int i = 0 ; i < N_THREADS ; i++) {
 60             int delta = i;
 61             scope.addCloseAction(() -> acc.addAndGet(delta));
 62         }

141         threads.forEach(t -> {
142             try {
143                 t.join();
144             } catch (InterruptedException ex) {
145                 fail();
146             }
147         });
148 
149         if (cleaner == null) {
150             assertEquals(acc.get(), IntStream.range(0, N_THREADS).sum());
151         } else {
152             scope = null;
153             scopeRef.set(null);
154             int expected = IntStream.range(0, N_THREADS).sum();
155             while (acc.get() != expected) {
156                 kickGC();
157             }
158         }
159     }
160 
161     @Test
162     public void testLockSingleThread() {
163         ResourceScope scope = ResourceScope.newConfinedScope();
164         List<ResourceScope> handles = new ArrayList<>();



165         for (int i = 0 ; i < N_THREADS ; i++) {
166             ResourceScope handle = ResourceScope.newConfinedScope();
167             handle.keepAlive(scope);
168             handles.add(handle);
169         }
170 
171         while (true) {
172             try {
173                 scope.close();
174                 assertEquals(handles.size(), 0);
175                 break;
176             } catch (IllegalStateException ex) {
177                 assertTrue(handles.size() > 0);
178                 ResourceScope handle = handles.remove(0);
179                 handle.close();


180             }
181         }
182     }
183 
184     @Test
185     public void testLockSharedMultiThread() {
186         ResourceScope scope = ResourceScope.newSharedScope();



187         AtomicInteger lockCount = new AtomicInteger();
188         for (int i = 0 ; i < N_THREADS ; i++) {
189             new Thread(() -> {
190                 try (ResourceScope handle = ResourceScope.newConfinedScope()) {
191                     handle.keepAlive(scope);
192                     lockCount.incrementAndGet();
193                     waitSomeTime();
194                     lockCount.decrementAndGet();
195                     handle.close();


196                 } catch (IllegalStateException ex) {
197                     // might be already closed - do nothing
198                 }
199             }).start();
200         }
201 
202         while (true) {
203             try {
204                 scope.close();
205                 assertEquals(lockCount.get(), 0);
206                 break;
207             } catch (IllegalStateException ex) {
208                 waitSomeTime();
209             }
210         }
211     }
212 
213     @Test
214     public void testCloseEmptyConfinedScope() {
215         ResourceScope.newConfinedScope().close();
216     }
217 
218     @Test
219     public void testCloseEmptySharedScope() {
220         ResourceScope.newSharedScope().close();
221     }
222 
223     @Test
224     public void testCloseConfinedLock() {
225         ResourceScope scope = ResourceScope.newConfinedScope();
226         ResourceScope handle = ResourceScope.newConfinedScope();
227         handle.keepAlive(scope);
228         AtomicReference<Throwable> failure = new AtomicReference<>();
229         Thread t = new Thread(() -> {
230             try {
231                 handle.close();


232             } catch (Throwable ex) {
233                 failure.set(ex);
234             }
235         });
236         t.start();
237         try {
238             t.join();
239             assertNotNull(failure.get());
240             assertEquals(failure.get().getClass(), IllegalStateException.class);
241         } catch (Throwable ex) {
242             throw new AssertionError(ex);
243         }
244     }
245 
246     @Test(dataProvider = "scopes")
247     public void testScopeHandles(Supplier<ResourceScope> scopeFactory) {
248         ResourceScope scope = scopeFactory.get();
249         acquireRecursive(scope, 5);
250         if (scope != ResourceScope.globalScope()) {
251             scope.close();
252         }
253     }
254 
255     @Test(dataProvider = "scopes", expectedExceptions = IllegalArgumentException.class)
256     public void testAcquireSelf(Supplier<ResourceScope> scopeSupplier) {
257         ResourceScope scope = scopeSupplier.get();
258         scope.keepAlive(scope);
259     }
260 
261     private void acquireRecursive(ResourceScope scope, int acquireCount) {
262         try (ResourceScope handle = ResourceScope.newConfinedScope()) {
263             handle.keepAlive(scope);
264             if (acquireCount > 0) {
265                 // recursive acquire
266                 acquireRecursive(scope, acquireCount - 1);
267             }
268             if (scope != ResourceScope.globalScope()) {
269                 assertThrows(IllegalStateException.class, scope::close);
270             }
271         }
272     }
273 
274     @Test
275     public void testConfinedScopeWithImplicitDependency() {
276         ResourceScope root = ResourceScope.newConfinedScope();
277         // Create many implicit scopes which depend on 'root', and let them become unreachable.
278         for (int i = 0; i < N_THREADS; i++) {
279             ResourceScope.newConfinedScope(Cleaner.create()).keepAlive(root);
280         }
281         // Now let's keep trying to close 'root' until we succeed. This is trickier than it seems: cleanup action
282         // might be called from another thread (the Cleaner thread), so that the confined scope lock count is updated racily.
283         // If that happens, the loop below never terminates.
284         while (true) {
285             try {
286                 root.close();
287                 break; // success!
288             } catch (IllegalStateException ex) {
289                 kickGC();
290                 for (int i = 0 ; i < N_THREADS ; i++) {  // add more races from current thread
291                     try (ResourceScope scope = ResourceScope.newConfinedScope()) {
292                         scope.keepAlive(root);
293                         // dummy
294                     }
295                 }
296                 // try again
297             }
298         }
299     }
300 
301     @Test
302     public void testConfinedScopeWithSharedDependency() {
303         ResourceScope root = ResourceScope.newConfinedScope();
304         List<Thread> threads = new ArrayList<>();
305         // Create many implicit scopes which depend on 'root', and let them become unreachable.
306         for (int i = 0; i < N_THREADS; i++) {
307             ResourceScope scope = ResourceScope.newSharedScope(); // create scope inside same thread!
308             scope.keepAlive(root);
309             Thread t = new Thread(scope::close); // close from another thread!
310             threads.add(t);
311             t.start();
312         }
313         for (int i = 0 ; i < N_THREADS ; i++) { // add more races from current thread
314             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
315                 scope.keepAlive(root);
316                 // dummy
317             }
318         }
319         threads.forEach(t -> {
320             try {
321                 t.join();
322             } catch (InterruptedException ex) {
323                 // ok
324             }
325         });
326         // Now let's close 'root'. This is trickier than it seems: releases of the confined scope happen in different
327         // threads, so that the confined scope lock count is updated racily. If that happens, the following close will blow up.
328         root.close();
329     }
330 
331     private void waitSomeTime() {
332         try {
333             Thread.sleep(10);
334         } catch (InterruptedException ex) {
335             // ignore
336         }
337     }
338 
339     private void kickGC() {
340         for (int i = 0 ; i < 100 ; i++) {
341             byte[] b = new byte[100];
342             System.gc();
343             Thread.onSpinWait();
344         }
345     }
346 
347     @DataProvider
348     static Object[][] cleaners() {
< prev index next >