package aQute.lib.concurrentinit; /** * Helper class to handle concurrent system where you need to initialize a * value. The first one should create the value but the others should block * until the value has been created. Since we do not want to hold a lock during * the creation this is kind of tricky. This class uses a single monitor * {@link #lock} that oeprates a state machine. * * @param <T> */ public abstract class ConcurrentInitialize<T> { enum State { /* * Initial state, the first one that detects the machine is in this * state must create the object */ INIT, /* * The object is being created, block until errored or created */ CREATING, /* * There is an object, just return it */ DONE, /* * There was an error during creation, throw the error */ ERROR }; private State state = State.INIT; private T value; private Object lock = new Object(); private Thread creatingThread; private Exception exception; /** * Get the value or wait until it is created. */ public T get() throws Exception { synchronized (lock) { switch (state) { case INIT : state = State.CREATING; creatingThread = Thread.currentThread(); break; case CREATING : { if (creatingThread == Thread.currentThread()) throw new IllegalStateException( "Cycle: ConcurrentInitialize's create returns to same instance"); do { lock.wait(); } while (state == State.CREATING); if (state == State.ERROR) throw exception; return value; } case ERROR : throw exception; case DONE : return value; } } try { set(create(), null, State.DONE); return value; } catch (Exception e) { set(null, e, State.ERROR); throw e; } } private void set(T value, Exception e, State state) { synchronized (lock) { this.value = value; this.state = state; this.creatingThread = null; this.lock.notifyAll(); this.exception = e; } } /** * Override to create the actual object * * @return the actual object, could be null * @throws Exception if the creation failed this is the exception that was * thrown */ public abstract T create() throws Exception; }