import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;

public class GetAndSetLoops2 {

    static final long NPS = (1000L * 1000 * 1000);
    static final int NCPUS = Runtime.getRuntime().availableProcessors();
    static final int ITERS = 1 << 25;
    static final Executor exec = new ForkJoinPool();

    public static void main(String[] args) throws Exception {
        for (int reps = 0; reps < 2; ++reps) {
            for (int n = 1; n <= NCPUS; ++n) {
                System.out.printf("N : %3d", n);
                testRelease(n);
                testVol(n);
                testGetSet(n);
                testCasWrite(n);
                System.out.println();
            }
        }
    }

    static void testGetSet(int n) {
        Phaser p = new Phaser(n+1);
        IntVar v = new IntVar();
        for (int i = 0; i < n; ++i)
            exec.execute(new GetSetWriter(p, v, new IntVar()));
        long startTime = System.nanoTime();
        p.arriveAndAwaitAdvance();
        p.arriveAndAwaitAdvance();
        if (v.val != ITERS - 1)
            throw new Error();
        long elapsed = System.nanoTime() - startTime;
        double secs = ((double)elapsed) / NPS;
        System.out.printf(" GetSet: %7.2f", secs);
    }

    static void testVol(int n) {
        Phaser p = new Phaser(n+1);
        IntVar v = new IntVar();
        for (int i = 0; i < n; ++i)
            exec.execute(new VolWriter(p, v, new IntVar()));
        long startTime = System.nanoTime();
        p.arriveAndAwaitAdvance();
        p.arriveAndAwaitAdvance();
        if (v.val != ITERS - 1)
            throw new Error();
        long elapsed = System.nanoTime() - startTime;
        double secs = ((double)elapsed) / NPS;
        System.out.printf(" Volatile: %7.2f", secs);
    }

    static void testCasWrite(int n) {
        Phaser p = new Phaser(n+1);
        IntVar v = new IntVar();
        for (int i = 0; i < n; ++i)
            exec.execute(new CasWriter(p, v, new IntVar()));
        long startTime = System.nanoTime();
        p.arriveAndAwaitAdvance();
        p.arriveAndAwaitAdvance();
        if (v.val != ITERS - 1)
            throw new Error();
        long elapsed = System.nanoTime() - startTime;
        double secs = ((double)elapsed) / NPS;
        System.out.printf(" CasWrite: %7.2f", secs);
    }
    
    static void testRelease(int n) {
        Phaser p = new Phaser(n+1);
        IntVar v = new IntVar();
        for (int i = 0; i < n; ++i)
            exec.execute(new ReleaseWriter(p, v, new IntVar()));
        long startTime = System.nanoTime();
        p.arriveAndAwaitAdvance();
        p.arriveAndAwaitAdvance();
        if (v.val != ITERS - 1)
            throw new Error();
        long elapsed = System.nanoTime() - startTime;
        double secs = ((double)elapsed) / NPS;
        System.out.printf(" Release: %7.2f", secs);
    }
    
    static final class IntVar {
        volatile int p01, p02, p03, p04, p05, p06, p07, p08, p09, p0a, p0b, p0c, p0d, p0e;
        volatile int val;
        volatile int q01, q02, q03, q04, q05, q06, q07, q08, q09, q0a, q0b, q0c, q0d, q0e;
        private static final VarHandle VAL;
        static {
            try {
                MethodHandles.Lookup l = MethodHandles.lookup();
                VAL = l.findVarHandle(IntVar.class, "val", int.class);
            } catch (Exception e) {
                throw new Error(e);
            }
        }

        void setVolatile(int x) {
            VAL.setVolatile(this, x);
        }

        void setRelease(int x) {
            VAL.setRelease(this, x);
        }
        
        void getAndSet(int x) {
            VAL.getAndSet(this, x);
        }

        void casWrite(int x) {
            while (!VAL.compareAndSet(this, val, x));
        }
    }
    
    static final class GetSetWriter implements Runnable {
        final Phaser phaser;
        final IntVar var;
        final IntVar loc;
        GetSetWriter(Phaser p, IntVar v, IntVar l) { phaser = p; var = v; loc = l; }
        public void run() {
            try {
                phaser.arriveAndAwaitAdvance();
                for (int i = 0; i < ITERS; ++i)
                    loc.getAndSet(i);
                var.getAndSet(ITERS - 1);
            } catch(Exception e) {
            } finally {
                phaser.arrive();
            }
        }
    }

    static final class ReleaseWriter implements Runnable {
        final Phaser phaser;
        final IntVar var;
        final IntVar loc;
        ReleaseWriter(Phaser p, IntVar v, IntVar l) { phaser = p; var = v; loc = l; }
        public void run() {
            try {
                phaser.arriveAndAwaitAdvance();
                for (int i = 0; i < ITERS; ++i)
                    loc.setRelease(i);
                var.setRelease(ITERS - 1);
            } catch(Exception e) {
            } finally {
                phaser.arrive();
            }
        }
    }
    
    static final class VolWriter implements Runnable {
        final Phaser phaser;
        final IntVar var;
        final IntVar loc;
        VolWriter(Phaser p, IntVar v, IntVar l) { phaser = p; var = v; loc = l; }
        public void run() {
            try {
                phaser.arriveAndAwaitAdvance();
                for (int i = 0; i < ITERS; ++i)
                    loc.setVolatile(i);
                var.setVolatile(ITERS - 1);
            } catch(Exception e) {
            } finally {
                phaser.arrive();
            }
        }
    }

    static final class CasWriter implements Runnable {
        final Phaser phaser;
        final IntVar var;
        final IntVar loc;
        CasWriter(Phaser p, IntVar v, IntVar l) { phaser = p; var = v; loc = l; }
        public void run() {
            try {
                phaser.arriveAndAwaitAdvance();
                for (int i = 0; i < ITERS; ++i)
                    loc.casWrite(i);
                var.casWrite(ITERS -1 );
            } catch(Exception e) {
            } finally {
                phaser.arrive();
            }
        }
    }
    
}
