< prev index next >

test/jdk/java/util/concurrent/forkjoin/FJExceptionTableLeak.java

Print this page
8246585: ForkJoin updates
Reviewed-by: martin


  45 import org.testng.annotations.Test;
  46 
  47 import static java.util.concurrent.TimeUnit.MILLISECONDS;
  48 
  49 import java.lang.ref.ReferenceQueue;
  50 import java.lang.ref.WeakReference;
  51 import java.lang.invoke.MethodHandles;
  52 import java.lang.invoke.VarHandle;
  53 import java.util.ArrayList;
  54 import java.util.concurrent.CountDownLatch;
  55 import java.util.concurrent.ForkJoinPool;
  56 import java.util.concurrent.ForkJoinTask;
  57 import java.util.concurrent.RecursiveAction;
  58 import java.util.concurrent.ThreadLocalRandom;
  59 import java.util.concurrent.locks.ReentrantLock;
  60 import java.util.function.BooleanSupplier;
  61 
  62 @Test
  63 public class FJExceptionTableLeak {
  64     final ThreadLocalRandom rnd = ThreadLocalRandom.current();
  65     final VarHandle NEXT, EX;
  66     final Object[] exceptionTable;
  67     final ReentrantLock exceptionTableLock;
  68 
  69     FJExceptionTableLeak() throws ReflectiveOperationException {




  70         MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(
  71             ForkJoinTask.class, MethodHandles.lookup());
  72         Class<?> nodeClass = Class.forName(
  73             ForkJoinTask.class.getName() + "$ExceptionNode");
  74         VarHandle exceptionTableHandle = lookup.findStaticVarHandle(
  75             ForkJoinTask.class, "exceptionTable", arrayClass(nodeClass));
  76         VarHandle exceptionTableLockHandle = lookup.findStaticVarHandle(
  77             ForkJoinTask.class, "exceptionTableLock", ReentrantLock.class);
  78         exceptionTable = (Object[]) exceptionTableHandle.get();
  79         exceptionTableLock = (ReentrantLock) exceptionTableLockHandle.get();
  80 
  81         NEXT = lookup.findVarHandle(nodeClass, "next", nodeClass);
  82         EX = lookup.findVarHandle(nodeClass, "ex", Throwable.class);
  83     }
  84 
  85     static <T> Class<T[]> arrayClass(Class<T> klazz) {
  86         try {
  87             return (Class<T[]>) Class.forName("[L" + klazz.getName() + ";");
  88         } catch (ReflectiveOperationException ex) {
  89             throw new Error(ex);


  98         public void compute() { throw new FailingTaskException(); }
  99     }
 100 
 101     /** Counts all FailingTaskExceptions still recorded in exceptionTable. */
 102     int retainedExceptions() {
 103         exceptionTableLock.lock();
 104         try {
 105             int count = 0;
 106             for (Object node : exceptionTable)
 107                 for (; node != null; node = next(node))
 108                     if (ex(node) instanceof FailingTaskException)
 109                         count++;
 110             return count;
 111         } finally {
 112             exceptionTableLock.unlock();
 113         }
 114     }
 115 
 116     @Test
 117     public void exceptionTableCleanup() throws Exception {





 118         ArrayList<FailingTask> failedTasks = failedTasks();
 119 
 120         // Retain a strong ref to one last failing task
 121         FailingTask lastTask = failedTasks.get(rnd.nextInt(failedTasks.size()));
 122 
 123         // Clear all other strong refs, making exception table cleanable
 124         failedTasks.clear();
 125 
 126         BooleanSupplier exceptionTableIsClean = () -> {
 127             try {
 128                 // Trigger exception table expunging as side effect
 129                 lastTask.join();
 130                 throw new AssertionError("should throw");
 131             } catch (FailingTaskException expected) {}
 132             int count = retainedExceptions();
 133             if (count == 0)
 134                 throw new AssertionError("expected to find last task");
 135             return count == 1;
 136         };
 137         gcAwait(exceptionTableIsClean);




  45 import org.testng.annotations.Test;
  46 
  47 import static java.util.concurrent.TimeUnit.MILLISECONDS;
  48 
  49 import java.lang.ref.ReferenceQueue;
  50 import java.lang.ref.WeakReference;
  51 import java.lang.invoke.MethodHandles;
  52 import java.lang.invoke.VarHandle;
  53 import java.util.ArrayList;
  54 import java.util.concurrent.CountDownLatch;
  55 import java.util.concurrent.ForkJoinPool;
  56 import java.util.concurrent.ForkJoinTask;
  57 import java.util.concurrent.RecursiveAction;
  58 import java.util.concurrent.ThreadLocalRandom;
  59 import java.util.concurrent.locks.ReentrantLock;
  60 import java.util.function.BooleanSupplier;
  61 
  62 @Test
  63 public class FJExceptionTableLeak {
  64     final ThreadLocalRandom rnd = ThreadLocalRandom.current();
  65     VarHandle NEXT, EX;
  66     Object[] exceptionTable;
  67     ReentrantLock exceptionTableLock;
  68 
  69     FJExceptionTableLeak() {
  70         // initialize separately to allow to pass with FJ versions without table
  71     }
  72 
  73     void init() throws ReflectiveOperationException {
  74         MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(
  75             ForkJoinTask.class, MethodHandles.lookup());
  76         Class<?> nodeClass = Class.forName(
  77             ForkJoinTask.class.getName() + "$ExceptionNode");
  78         VarHandle exceptionTableHandle = lookup.findStaticVarHandle(
  79             ForkJoinTask.class, "exceptionTable", arrayClass(nodeClass));
  80         VarHandle exceptionTableLockHandle = lookup.findStaticVarHandle(
  81             ForkJoinTask.class, "exceptionTableLock", ReentrantLock.class);
  82         exceptionTable = (Object[]) exceptionTableHandle.get();
  83         exceptionTableLock = (ReentrantLock) exceptionTableLockHandle.get();
  84 
  85         NEXT = lookup.findVarHandle(nodeClass, "next", nodeClass);
  86         EX = lookup.findVarHandle(nodeClass, "ex", Throwable.class);
  87     }
  88 
  89     static <T> Class<T[]> arrayClass(Class<T> klazz) {
  90         try {
  91             return (Class<T[]>) Class.forName("[L" + klazz.getName() + ";");
  92         } catch (ReflectiveOperationException ex) {
  93             throw new Error(ex);


 102         public void compute() { throw new FailingTaskException(); }
 103     }
 104 
 105     /** Counts all FailingTaskExceptions still recorded in exceptionTable. */
 106     int retainedExceptions() {
 107         exceptionTableLock.lock();
 108         try {
 109             int count = 0;
 110             for (Object node : exceptionTable)
 111                 for (; node != null; node = next(node))
 112                     if (ex(node) instanceof FailingTaskException)
 113                         count++;
 114             return count;
 115         } finally {
 116             exceptionTableLock.unlock();
 117         }
 118     }
 119 
 120     @Test
 121     public void exceptionTableCleanup() throws Exception {
 122         try {
 123             init();
 124         } catch (ReflectiveOperationException ex) {
 125             return; // using FJ Version without Exception table
 126         }
 127         ArrayList<FailingTask> failedTasks = failedTasks();
 128 
 129         // Retain a strong ref to one last failing task
 130         FailingTask lastTask = failedTasks.get(rnd.nextInt(failedTasks.size()));
 131 
 132         // Clear all other strong refs, making exception table cleanable
 133         failedTasks.clear();
 134 
 135         BooleanSupplier exceptionTableIsClean = () -> {
 136             try {
 137                 // Trigger exception table expunging as side effect
 138                 lastTask.join();
 139                 throw new AssertionError("should throw");
 140             } catch (FailingTaskException expected) {}
 141             int count = retainedExceptions();
 142             if (count == 0)
 143                 throw new AssertionError("expected to find last task");
 144             return count == 1;
 145         };
 146         gcAwait(exceptionTableIsClean);


< prev index next >