/** Enters this monitor when the guard is satisfied. Blocks indefinitely. */ public void enterWhenUninterruptibly(Guard guard) { if (guard.monitor != this) { throw new IllegalMonitorStateException(); } final ReentrantLock lock = this.lock; boolean signalBeforeWaiting = lock.isHeldByCurrentThread(); lock.lock(); boolean satisfied = false; try { if (!guard.isSatisfied()) { awaitUninterruptibly(guard, signalBeforeWaiting); } satisfied = true; } finally { if (!satisfied) { leave(); } } }
/** * Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the * lock, but does not wait for the guard to be satisfied. * * @return whether the monitor was entered, which guarantees that the guard is now satisfied */ @SuppressWarnings("GoodTime") // should accept a java.time.Duration public boolean enterIf(Guard guard, long time, TimeUnit unit) { if (guard.monitor != this) { throw new IllegalMonitorStateException(); } if (!enter(time, unit)) { return false; } boolean satisfied = false; try { return satisfied = guard.isSatisfied(); } finally { if (!satisfied) { lock.unlock(); } } }
@Override public final void awaitRunning() { monitor.enterWhenUninterruptibly(hasReachedRunning); try { checkCurrentState(RUNNING); } finally { monitor.leave(); } }
void awaitStopped() { monitor.enterWhenUninterruptibly(stoppedGuard); monitor.leave(); }
/** * Attempts to start the timer immediately prior to the service being started via {@link * Service#startAsync()}. */ void tryStartTiming(Service service) { monitor.enter(); try { Stopwatch stopwatch = startupTimers.get(service); if (stopwatch == null) { startupTimers.put(service, Stopwatch.createStarted()); } } finally { monitor.leave(); } }
@GuardedBy("lock") private void await(Guard guard, boolean signalBeforeWaiting) throws InterruptedException { if (signalBeforeWaiting) { signalNextWaiter(); } beginWaitingFor(guard); try { do { guard.condition.await(); } while (!guard.isSatisfied()); } finally { endWaitingFor(guard); } }
@GuardedBy("lock") private void awaitUninterruptibly(Guard guard, boolean signalBeforeWaiting) { if (signalBeforeWaiting) { signalNextWaiter(); } beginWaitingFor(guard); try { do { guard.condition.awaitUninterruptibly(); } while (!guard.isSatisfied()); } finally { endWaitingFor(guard); } }
/** * Exactly like guard.isSatisfied(), but in addition signals all waiting threads in the * (hopefully unlikely) event that isSatisfied() throws. */ @GuardedBy("lock") private boolean isSatisfied(Guard guard) { try { return guard.isSatisfied(); } catch (Throwable throwable) { signalAllWaiters(); throw Throwables.propagate(throwable); } }
/** * Enters this monitor when the guard is satisfied. Blocks indefinitely, but may be interrupted. * * @throws InterruptedException if interrupted while waiting */ public void enterWhen(Guard guard) throws InterruptedException { if (guard.monitor != this) { throw new IllegalMonitorStateException(); } final ReentrantLock lock = this.lock; boolean signalBeforeWaiting = lock.isHeldByCurrentThread(); lock.lockInterruptibly(); boolean satisfied = false; try { if (!guard.isSatisfied()) { await(guard, signalBeforeWaiting); } satisfied = true; } finally { if (!satisfied) { leave(); } } }
/** Caller should check before calling that guard is not satisfied. */ @GuardedBy("lock") private boolean awaitNanos(Guard guard, long nanos, boolean signalBeforeWaiting) throws InterruptedException { boolean firstTime = true; try { do { if (nanos <= 0L) { return false; } if (firstTime) { if (signalBeforeWaiting) { signalNextWaiter(); } beginWaitingFor(guard); firstTime = false; } nanos = guard.condition.awaitNanos(nanos); } while (!guard.isSatisfied()); return true; } finally { if (!firstTime) { endWaitingFor(guard); } } } }
@CanIgnoreReturnValue @Override public final Service stopAsync() { if (monitor.enterIf(isStoppable)) { try { State previous = state(); switch (previous) { case NEW: snapshot = new StateSnapshot(TERMINATED); enqueueTerminatedEvent(NEW); break; case STARTING: snapshot = new StateSnapshot(STARTING, true, null); enqueueStoppingEvent(STARTING); doCancelStart(); break; notifyFailed(shutdownFailure); } finally { monitor.leave(); dispatchListenerEvents();
@CanIgnoreReturnValue @Override public final Service startAsync() { if (monitor.enterIf(isStartable)) { try { snapshot = new StateSnapshot(STARTING); enqueueStartingEvent(); doStart(); } catch (Throwable startupFailure) { notifyFailed(startupFailure); } finally { monitor.leave(); dispatchListenerEvents(); } } else { throw new IllegalStateException("Service " + this + " has already been started"); } return this; }
monitor.enter(); try { new IllegalStateException( "Cannot notifyStarted() when the service is " + snapshot.state); notifyFailed(failure); throw failure; doStop(); } else { snapshot = new StateSnapshot(RUNNING); enqueueRunningEvent(); monitor.leave(); dispatchListenerEvents();
/** * Implementing classes should invoke this method once their service has stopped. It will cause * the service to transition from {@link State#STARTING} or {@link State#STOPPING} to {@link * State#TERMINATED}. * * @throws IllegalStateException if the service is not one of {@link State#STOPPING}, {@link * State#STARTING}, or {@link State#RUNNING}. */ protected final void notifyStopped() { monitor.enter(); try { State previous = state(); switch (previous) { case NEW: case TERMINATED: case FAILED: throw new IllegalStateException("Cannot notifyStopped() when the service is " + previous); case RUNNING: case STARTING: case STOPPING: snapshot = new StateSnapshot(TERMINATED); enqueueTerminatedEvent(previous); break; } } finally { monitor.leave(); dispatchListenerEvents(); } }
/** * Invoke this method to transition the service to the {@link State#FAILED}. The service will * <b>not be stopped</b> if it is running. Invoke this method when a service has failed critically * or otherwise cannot be started nor stopped. */ protected final void notifyFailed(Throwable cause) { checkNotNull(cause); monitor.enter(); try { State previous = state(); switch (previous) { case NEW: case TERMINATED: throw new IllegalStateException("Failed while in state:" + previous, cause); case RUNNING: case STARTING: case STOPPING: snapshot = new StateSnapshot(FAILED, false, cause); enqueueFailedEvent(previous, cause); break; case FAILED: // Do nothing break; } } finally { monitor.leave(); dispatchListenerEvents(); } }
@Override public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { if (monitor.enterWhenUninterruptibly(isStopped, timeout, unit)) { try { checkCurrentState(TERMINATED); } finally { monitor.leave(); } } else { // It is possible due to races the we are currently in the expected state even though we // timed out. e.g. if we weren't event able to grab the lock within the timeout we would never // even check the guard. I don't think we care too much about this use case but it could lead // to a confusing error message. throw new TimeoutException( "Timed out waiting for " + this + " to reach a terminal state. " + "Current state: " + state()); } }
void awaitStopped(long timeout, TimeUnit unit) throws TimeoutException { monitor.enter(); try { if (!monitor.waitForUninterruptibly(stoppedGuard, timeout, unit)) { throw new TimeoutException( "Timeout waiting for the services to stop. The following " + "services have not stopped: " + Multimaps.filterKeys(servicesByState, not(in(EnumSet.of(TERMINATED, FAILED))))); } } finally { monitor.leave(); } }
@Deprecated @Override public final ListenableFuture<State> start() { if (monitor.enterIf(isStartable)) { try { snapshot = new StateSnapshot(STARTING); starting(); doStart(); } catch (Throwable startupFailure) { notifyFailed(startupFailure); } finally { monitor.leave(); executeListeners(); } } return startup; }
private void runWaitTest() { assertFalse(Thread.currentThread().isInterrupted()); assertFalse(monitor.isOccupiedByCurrentThread()); monitor.enter(); try { assertTrue(monitor.isOccupiedByCurrentThread()); doWaitScenarioSetUp(); boolean interruptedBeforeCall = Thread.currentThread().isInterrupted(); Outcome actualOutcome = doCall(); boolean occupiedAfterCall = monitor.isOccupiedByCurrentThread(); boolean interruptedAfterCall = Thread.currentThread().isInterrupted(); assertEquals(expectedOutcome, actualOutcome); assertTrue(occupiedAfterCall); assertEquals( interruptedBeforeCall && expectedOutcome != Outcome.INTERRUPT, interruptedAfterCall); } finally { guard.setSatisfied(true); monitor.leave(); assertFalse(monitor.isOccupiedByCurrentThread()); } }
private final Monitor monitor = new Monitor(); private final Monitor.Guard paused = new Monitor.Guard(monitor) { @Override monitor.enterWhenUninterruptibly(notPaused); try { monitor.waitForUninterruptibly(notPaused); } finally { monitor.leave(); monitor.enterIf(notPaused); try { isPaused = true; } finally { monitor.leave(); monitor.enterIf(paused); try { isPaused = false; } finally { monitor.leave();