| // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 | package sync; | 
 |  | 
 | import java.util.concurrent.ExecutionException; | 
 | import java.util.concurrent.ExecutorService; | 
 | import java.util.concurrent.Executors; | 
 | import java.util.concurrent.Future; | 
 | import java.util.concurrent.TimeUnit; | 
 |  | 
 | public class Sync { | 
 |  | 
 |   public static final int THREADS = 10; | 
 |   public static final int ITERATIONS = 10; | 
 |  | 
 |   private static final int INITIAL_SHARED_STATE = -1; | 
 |   private static final long SLEEP = 10; | 
 |  | 
 |   // Shared mutable state that is tested to be consistent | 
 |   private static int sharedState = INITIAL_SHARED_STATE; | 
 |   private static boolean shouldThrow = false; | 
 |  | 
 |   // Copy of interface java.util.function.Consumer to make this test work without a Java 8 runtime | 
 |   // library | 
 |   public interface Consumer<T> { | 
 |  | 
 |     void accept(T t); | 
 |   } | 
 |  | 
 |   public static void couldThrow(int index) { | 
 |     if (shouldThrow) throw new RuntimeException(); | 
 |     // Copy shared state and trash it (we set our index). | 
 |     int local = sharedState; | 
 |     sharedState = index; | 
 |     try { | 
 |       Thread.sleep(SLEEP); | 
 |     } catch (Throwable e) { | 
 |       throw new RuntimeException(e); | 
 |     } | 
 |     // Restore the shared state if it is still valid. | 
 |     if (sharedState == index) { | 
 |       sharedState = local; | 
 |     } | 
 |   } | 
 |  | 
 |   public static synchronized void staticSynchronized(int index) { | 
 |     System.out.println("static"); | 
 |     couldThrow(index); | 
 |     System.out.println("end"); | 
 |   } | 
 |  | 
 |   public synchronized void instanceSynchronized(int index) { | 
 |     System.out.println("instance"); | 
 |     couldThrow(index); | 
 |     System.out.println("end"); | 
 |   } | 
 |  | 
 |   public void manualSynchronized(int index) { | 
 |     System.out.println("manual"); | 
 |     synchronized (this) { | 
 |       couldThrow(index); | 
 |     } | 
 |     System.out.println("manual"); | 
 |   } | 
 |  | 
 |   public synchronized void tryCatchSynchronized(int index) { | 
 |     System.out.println("trycatch"); | 
 |     try { | 
 |       couldThrow(index); | 
 |       try { | 
 |         couldThrow(index); | 
 |       } finally { | 
 |         System.out.println("end"); | 
 |         return; | 
 |       } | 
 |     } catch (RuntimeException e) { | 
 |       System.out.println("caught & end"); | 
 |       return; | 
 |     } catch (Throwable e) { | 
 |       System.out.println("caught other"); | 
 |     } | 
 |     System.out.println("end"); | 
 |   } | 
 |  | 
 |   public static synchronized void throwStaticSynchronized() { | 
 |     throw new RuntimeException(); | 
 |   } | 
 |  | 
 |   public synchronized void throwInstanceSynchronized() { | 
 |     throw new RuntimeException(); | 
 |   } | 
 |  | 
 |   public static void run(ExecutorService service, final Consumer<Integer> fn) | 
 |       throws ExecutionException, InterruptedException { | 
 |     Future[] results = new Future[ITERATIONS]; | 
 |     for (int i = 0; i < ITERATIONS; ++i) { | 
 |       final int index = i; | 
 |       results[i] = service.submit(new Runnable() { | 
 |         @Override | 
 |         public void run() { | 
 |           fn.accept(index); | 
 |         } | 
 |       }); | 
 |     } | 
 |     for (Future result : results) { | 
 |       result.get(); | 
 |     } | 
 |     if (sharedState != INITIAL_SHARED_STATE) { | 
 |       throw new RuntimeException("Synchronization error!"); | 
 |     } | 
 |   } | 
 |  | 
 |   public static void main(String[] args) throws ExecutionException, InterruptedException { | 
 |     shouldThrow = args.length > 100; | 
 |     ExecutorService service = Executors.newFixedThreadPool(THREADS); | 
 |     run(service, new Consumer<Integer>() { | 
 |       @Override | 
 |       public void accept(Integer index) { | 
 |          Sync.staticSynchronized(index); | 
 |       } | 
 |     }); | 
 |     final Sync sync = new Sync(); | 
 |     run(service, new Consumer<Integer>() { | 
 |       @Override | 
 |       public void accept(Integer index) { | 
 |         sync.instanceSynchronized(index); | 
 |       } | 
 |     }); | 
 |     run(service, new Consumer<Integer>() { | 
 |       @Override | 
 |       public void accept(Integer index) { | 
 |         sync.manualSynchronized(index); | 
 |       } | 
 |     }); | 
 |     run(service, new Consumer<Integer>() { | 
 |       @Override | 
 |       public void accept(Integer index) { | 
 |         sync.tryCatchSynchronized(index); | 
 |       } | 
 |     }); | 
 |     service.shutdown(); | 
 |     service.awaitTermination(5, TimeUnit.SECONDS); | 
 |     try { | 
 |       Sync.throwStaticSynchronized(); | 
 |       throw new Error("expected throw"); | 
 |     } catch (RuntimeException e) { | 
 |       System.out.println("caught throw"); | 
 |     } | 
 |     try { | 
 |       sync.throwInstanceSynchronized(); | 
 |       throw new Error("expected throw"); | 
 |     } catch (RuntimeException e) { | 
 |       System.out.println("caught throw"); | 
 |     } | 
 |   } | 
 | } |