|  | // 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"); | 
|  | } | 
|  | } | 
|  | } |