001package org.slf4j.basicTests;
002
003import java.util.concurrent.BrokenBarrierException;
004import java.util.concurrent.CyclicBarrier;
005
006/**
007 * This class demonstrates that threads accessing the STATE variable always see a consistent value. 
008 * 
009 * During ongoing initialization the observed value is either ONGOING_INITIALIZATION
010 * or one of {SUCCESS, FAILURE}. 
011 * 
012 * Post initialization the observed value is always one of  {SUCCESS, FAILURE}.
013 * 
014 * See also http://jira.qos.ch/browse/SLF4J-167 
015 * 
016 * @author ceki
017 *
018 */
019public class DoubleCheckedInt {
020
021    final static int THREAD_COUNT = 10 + Runtime.getRuntime().availableProcessors() * 2;
022    final static int UNINITIALIZED_STATE = 0;
023    final static int ONGOING_INITIALIZATION = 1;
024    final static int SUCCESS = 2;
025    final static int FAILURE = 3;
026    final static int NUMBER_OF_STATES = FAILURE + 1;
027
028    private static int STATE = UNINITIALIZED_STATE;
029
030    public static int getState() {
031        if (STATE == 0) {
032            synchronized (DoubleCheckedInt.class) {
033                if (STATE == UNINITIALIZED_STATE) {
034                    STATE = ONGOING_INITIALIZATION;
035                    long r = System.nanoTime();
036                    try {
037                        Thread.sleep(10);
038                    } catch (InterruptedException e) {
039                    }
040                    if (r % 2 == 0) {
041                        STATE = SUCCESS;
042                    } else {
043                        STATE = FAILURE;
044                    }
045                }
046            }
047        }
048        return STATE;
049    }
050
051    static public void main(String[] args) throws InterruptedException, BrokenBarrierException {
052        StateAccessingThread[] preInitializationThreads = harness();
053        check(preInitializationThreads, false);
054
055        System.out.println("============");
056        StateAccessingThread[] postInitializationThreads = harness();
057        check(postInitializationThreads, true);
058    }
059
060    private static StateAccessingThread[] harness() throws InterruptedException, BrokenBarrierException {
061        StateAccessingThread[] threads = new StateAccessingThread[THREAD_COUNT];
062        final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
063        for (int i = 0; i < THREAD_COUNT; i++) {
064            threads[i] = new StateAccessingThread(barrier);
065            threads[i].start();
066        }
067
068        barrier.await();
069        for (int i = 0; i < THREAD_COUNT; i++) {
070            threads[i].join();
071        }
072        return threads;
073    }
074
075    private static void check(StateAccessingThread[] threads, boolean postInit) {
076
077        int[] stateCount = getStateCount(threads);
078        printStateCount(stateCount);
079
080        if (stateCount[UNINITIALIZED_STATE] != 0) {
081            throw new IllegalStateException("getState() should never return a zero value");
082        }
083
084        if (stateCount[SUCCESS] != 0 && stateCount[FAILURE] != 0) {
085            throw new IllegalStateException("getState() should return consistent values");
086        }
087
088        if (postInit) {
089            if (stateCount[SUCCESS] != THREAD_COUNT && stateCount[FAILURE] != THREAD_COUNT) {
090                throw new IllegalStateException("getState() should return consistent values");
091            }
092        }
093
094    }
095
096    private static void printStateCount(int[] stateCount) {
097        for (int i = 0; i < NUMBER_OF_STATES; i++) {
098            switch (i) {
099            case UNINITIALIZED_STATE:
100                System.out.println("UNINITIALIZED_STATE count: " + stateCount[i]);
101                break;
102            case ONGOING_INITIALIZATION:
103                System.out.println("ONGOING_INITIALIZATION count: " + stateCount[i]);
104                break;
105            case SUCCESS:
106                System.out.println("SUCCESS count: " + stateCount[i]);
107                break;
108            case FAILURE:
109                System.out.println("FAILURE count: " + stateCount[i]);
110                break;
111            }
112        }
113    }
114
115    private static int[] getStateCount(StateAccessingThread[] threads) {
116        int[] valCount = new int[NUMBER_OF_STATES];
117        for (StateAccessingThread thread : threads) {
118            int val = thread.state;
119            valCount[val] = valCount[val] + 1;
120        }
121        return valCount;
122    }
123
124    static class StateAccessingThread extends Thread {
125        public int state = -1;
126        final CyclicBarrier barrier;
127
128        StateAccessingThread(CyclicBarrier barrier) {
129            this.barrier = barrier;
130        }
131
132        public void run() {
133            try {
134                barrier.await();
135            } catch (Exception e) {
136                e.printStackTrace();
137            }
138            state = DoubleCheckedInt.getState();
139        }
140    };
141}