blob: 42a39e64189391450600b1ccd5937bec3be86543 [file] [log] [blame]
package org.slf4j.basicTests;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* This class demonstrates that threads accessing the STATE variable always see a consistent value.
*
* During ongoing initialization the observed value is either ONGOING_INITIALIZATION
* or one of {SUCCESS, FAILURE}.
*
* Post initialization the observed value is always one of {SUCCESS, FAILURE}.
*
* See also http://jira.qos.ch/browse/SLF4J-167
*
* @author ceki
*
*/
public class DoubleCheckedInt {
final static int THREAD_COUNT = 10 + Runtime.getRuntime().availableProcessors() * 2;
final static int UNINITIALIZED_STATE = 0;
final static int ONGOING_INITIALIZATION = 1;
final static int SUCCESS = 2;
final static int FAILURE = 3;
final static int NUMBER_OF_STATES = FAILURE + 1;
private static int STATE = UNINITIALIZED_STATE;
public static int getState() {
if (STATE == 0) {
synchronized (DoubleCheckedInt.class) {
if (STATE == UNINITIALIZED_STATE) {
STATE = ONGOING_INITIALIZATION;
long r = System.nanoTime();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
if (r % 2 == 0) {
STATE = SUCCESS;
} else {
STATE = FAILURE;
}
}
}
}
return STATE;
}
static public void main(String[] args) throws InterruptedException, BrokenBarrierException {
StateAccessingThread[] preInitializationThreads = harness();
check(preInitializationThreads, false);
System.out.println("============");
StateAccessingThread[] postInitializationThreads = harness();
check(postInitializationThreads, true);
}
private static StateAccessingThread[] harness() throws InterruptedException, BrokenBarrierException {
StateAccessingThread[] threads = new StateAccessingThread[THREAD_COUNT];
final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = new StateAccessingThread(barrier);
threads[i].start();
}
barrier.await();
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].join();
}
return threads;
}
private static void check(StateAccessingThread[] threads, boolean postInit) {
int[] stateCount = getStateCount(threads);
printStateCount(stateCount);
if (stateCount[UNINITIALIZED_STATE] != 0) {
throw new IllegalStateException("getState() should never return a zero value");
}
if (stateCount[SUCCESS] != 0 && stateCount[FAILURE] != 0) {
throw new IllegalStateException("getState() should return consistent values");
}
if (postInit) {
if (stateCount[SUCCESS] != THREAD_COUNT && stateCount[FAILURE] != THREAD_COUNT) {
throw new IllegalStateException("getState() should return consistent values");
}
}
}
private static void printStateCount(int[] stateCount) {
for (int i = 0; i < NUMBER_OF_STATES; i++) {
switch (i) {
case UNINITIALIZED_STATE:
System.out.println("UNINITIALIZED_STATE count: " + stateCount[i]);
break;
case ONGOING_INITIALIZATION:
System.out.println("ONGOING_INITIALIZATION count: " + stateCount[i]);
break;
case SUCCESS:
System.out.println("SUCCESS count: " + stateCount[i]);
break;
case FAILURE:
System.out.println("FAILURE count: " + stateCount[i]);
break;
}
}
}
private static int[] getStateCount(StateAccessingThread[] threads) {
int[] valCount = new int[NUMBER_OF_STATES];
for (StateAccessingThread thread : threads) {
int val = thread.state;
valCount[val] = valCount[val] + 1;
}
return valCount;
}
static class StateAccessingThread extends Thread {
public int state = -1;
final CyclicBarrier barrier;
StateAccessingThread(CyclicBarrier barrier) {
this.barrier = barrier;
}
public void run() {
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
state = DoubleCheckedInt.getState();
}
};
}