< prev index next >

test/jdk/java/foreign/TestResourceScope.java

Print this page
*** 37,10 ***
--- 37,11 ---
  import org.testng.annotations.Test;
  import static org.testng.Assert.*;
  
  import java.util.ArrayList;
  import java.util.List;
+ import java.util.Set;
  import java.util.concurrent.atomic.AtomicInteger;
  import java.util.concurrent.atomic.AtomicReference;
  import java.util.function.Supplier;
  import java.util.stream.IntStream;
  

*** 155,53 ***
                  kickGC();
              }
          }
      }
  
!     @Test(dataProvider = "cleaners")
!     public void testLockSingleThread(Supplier<Cleaner> cleanerSupplier) {
!         Cleaner cleaner = cleanerSupplier.get();
!         ResourceScope scope = cleaner != null ?
-                 ResourceScope.newConfinedScope(cleaner) :
-                 ResourceScope.newConfinedScope();
-         List<ResourceScope.Handle> handles = new ArrayList<>();
          for (int i = 0 ; i < N_THREADS ; i++) {
!             handles.add(scope.acquire());
          }
  
          while (true) {
              try {
                  scope.close();
                  assertEquals(handles.size(), 0);
                  break;
              } catch (IllegalStateException ex) {
                  assertTrue(handles.size() > 0);
!                 ResourceScope.Handle handle = handles.remove(0);
!                 scope.release(handle);
-                 scope.release(handle); // make sure it's idempotent
-                 scope.release(handle); // make sure it's idempotent
              }
          }
      }
  
!     @Test(dataProvider = "cleaners")
!     public void testLockSharedMultiThread(Supplier<Cleaner> cleanerSupplier) {
!         Cleaner cleaner = cleanerSupplier.get();
-         ResourceScope scope = cleaner != null ?
-                 ResourceScope.newSharedScope(cleaner) :
-                 ResourceScope.newSharedScope();
          AtomicInteger lockCount = new AtomicInteger();
          for (int i = 0 ; i < N_THREADS ; i++) {
              new Thread(() -> {
!                 try {
!                     ResourceScope.Handle handle = scope.acquire(); // this can throw if segment has been closed
                      lockCount.incrementAndGet();
                      waitSomeTime();
                      lockCount.decrementAndGet();
!                     scope.release(handle); // cannot throw (acquired segments cannot be closed)
-                     scope.release(handle); // cannot throw (idempotent)
-                     scope.release(handle); // cannot throw (idempotent)
                  } catch (IllegalStateException ex) {
                      // might be already closed - do nothing
                  }
              }).start();
          }
--- 156,45 ---
                  kickGC();
              }
          }
      }
  
!     @Test
!     public void testLockSingleThread() {
!         ResourceScope scope = ResourceScope.newConfinedScope();
!         List<ResourceScope> handles = new ArrayList<>();
          for (int i = 0 ; i < N_THREADS ; i++) {
!             ResourceScope handle = ResourceScope.newConfinedScope();
+             handle.keepAlive(scope);
+             handles.add(handle);
          }
  
          while (true) {
              try {
                  scope.close();
                  assertEquals(handles.size(), 0);
                  break;
              } catch (IllegalStateException ex) {
                  assertTrue(handles.size() > 0);
!                 ResourceScope handle = handles.remove(0);
!                 handle.close();
              }
          }
      }
  
!     @Test
!     public void testLockSharedMultiThread() {
!         ResourceScope scope = ResourceScope.newSharedScope();
          AtomicInteger lockCount = new AtomicInteger();
          for (int i = 0 ; i < N_THREADS ; i++) {
              new Thread(() -> {
!                 try (ResourceScope handle = ResourceScope.newConfinedScope()) {
!                     handle.keepAlive(scope);
                      lockCount.incrementAndGet();
                      waitSomeTime();
                      lockCount.decrementAndGet();
!                     handle.close();
                  } catch (IllegalStateException ex) {
                      // might be already closed - do nothing
                  }
              }).start();
          }

*** 228,17 ***
      }
  
      @Test
      public void testCloseConfinedLock() {
          ResourceScope scope = ResourceScope.newConfinedScope();
!         ResourceScope.Handle handle = scope.acquire();
          AtomicReference<Throwable> failure = new AtomicReference<>();
          Thread t = new Thread(() -> {
              try {
!                 scope.release(handle);
-                 scope.release(handle); // make sure it's idempotent
-                 scope.release(handle); // make sure it's idempotent
              } catch (Throwable ex) {
                  failure.set(ex);
              }
          });
          t.start();
--- 221,16 ---
      }
  
      @Test
      public void testCloseConfinedLock() {
          ResourceScope scope = ResourceScope.newConfinedScope();
!         ResourceScope handle = ResourceScope.newConfinedScope();
+         handle.keepAlive(scope);
          AtomicReference<Throwable> failure = new AtomicReference<>();
          Thread t = new Thread(() -> {
              try {
!                 handle.close();
              } catch (Throwable ex) {
                  failure.set(ex);
              }
          });
          t.start();

*** 253,28 ***
  
      @Test(dataProvider = "scopes")
      public void testScopeHandles(Supplier<ResourceScope> scopeFactory) {
          ResourceScope scope = scopeFactory.get();
          acquireRecursive(scope, 5);
!         if (!scope.isImplicit()) {
              scope.close();
          }
      }
  
      private void acquireRecursive(ResourceScope scope, int acquireCount) {
!         ResourceScope.Handle handle = scope.acquire();
!         assertEquals(handle.scope(), scope);
!         if (acquireCount > 0) {
!             // recursive acquire
!             acquireRecursive(scope, acquireCount - 1);
          }
!         if (!scope.isImplicit()) {
!             assertThrows(IllegalStateException.class, scope::close);
          }
!         scope.release(handle);
!         scope.release(handle); // make sure it's idempotent
!         scope.release(handle); // make sure it's idempotent
      }
  
      private void waitSomeTime() {
          try {
              Thread.sleep(10);
--- 245,89 ---
  
      @Test(dataProvider = "scopes")
      public void testScopeHandles(Supplier<ResourceScope> scopeFactory) {
          ResourceScope scope = scopeFactory.get();
          acquireRecursive(scope, 5);
!         if (scope != ResourceScope.globalScope()) {
              scope.close();
          }
      }
  
+     @Test(dataProvider = "scopes", expectedExceptions = IllegalArgumentException.class)
+     public void testAcquireSelf(Supplier<ResourceScope> scopeSupplier) {
+         ResourceScope scope = scopeSupplier.get();
+         scope.keepAlive(scope);
+     }
+ 
      private void acquireRecursive(ResourceScope scope, int acquireCount) {
!         try (ResourceScope handle = ResourceScope.newConfinedScope()) {
!             handle.keepAlive(scope);
!             if (acquireCount > 0) {
!                 // recursive acquire
!                 acquireRecursive(scope, acquireCount - 1);
+             }
+             if (scope != ResourceScope.globalScope()) {
+                 assertThrows(IllegalStateException.class, scope::close);
+             }
          }
!     }
! 
+     @Test
+     public void testConfinedScopeWithImplicitDependency() {
+         ResourceScope root = ResourceScope.newConfinedScope();
+         // Create many implicit scopes which depend on 'root', and let them become unreachable.
+         for (int i = 0; i < N_THREADS; i++) {
+             ResourceScope.newConfinedScope(Cleaner.create()).keepAlive(root);
          }
!         // Now let's keep trying to close 'root' until we succeed. This is trickier than it seems: cleanup action
!         // might be called from another thread (the Cleaner thread), so that the confined scope lock count is updated racily.
!         // If that happens, the loop below never terminates.
+         while (true) {
+             try {
+                 root.close();
+                 break; // success!
+             } catch (IllegalStateException ex) {
+                 kickGC();
+                 for (int i = 0 ; i < N_THREADS ; i++) {  // add more races from current thread
+                     try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+                         scope.keepAlive(root);
+                         // dummy
+                     }
+                 }
+                 // try again
+             }
+         }
+     }
+ 
+     @Test
+     public void testConfinedScopeWithSharedDependency() {
+         ResourceScope root = ResourceScope.newConfinedScope();
+         List<Thread> threads = new ArrayList<>();
+         // Create many implicit scopes which depend on 'root', and let them become unreachable.
+         for (int i = 0; i < N_THREADS; i++) {
+             ResourceScope scope = ResourceScope.newSharedScope(); // create scope inside same thread!
+             scope.keepAlive(root);
+             Thread t = new Thread(scope::close); // close from another thread!
+             threads.add(t);
+             t.start();
+         }
+         for (int i = 0 ; i < N_THREADS ; i++) { // add more races from current thread
+             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+                 scope.keepAlive(root);
+                 // dummy
+             }
+         }
+         threads.forEach(t -> {
+             try {
+                 t.join();
+             } catch (InterruptedException ex) {
+                 // ok
+             }
+         });
+         // Now let's close 'root'. This is trickier than it seems: releases of the confined scope happen in different
+         // threads, so that the confined scope lock count is updated racily. If that happens, the following close will blow up.
+         root.close();
      }
  
      private void waitSomeTime() {
          try {
              Thread.sleep(10);
< prev index next >