Skip to content

Commit

Permalink
Merge pull request #95 from pettermahlen/shutdown-race
Browse files Browse the repository at this point in the history
Ignore events and observation requests during shutdown
  • Loading branch information
pettermahlen authored Mar 5, 2020
2 parents ca50480 + 3c490e4 commit eb7f0e0
Showing 1 changed file with 32 additions and 4 deletions.
36 changes: 32 additions & 4 deletions mobius-core/src/main/java/com/spotify/mobius/MobiusLoop.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,17 @@ public class MobiusLoop<M, E, F> implements Loop<M, E, F> {

@Nullable private volatile M mostRecentModel;

private volatile boolean disposed;
private enum RunState {
// the loop is running normally
RUNNING,
// the loop is in the process of shutting down
DISPOSING,
// the loop has been shut down - any further attempts at interacting with it should be
// considered to be errors.
DISPOSED
}

private volatile RunState runState = RunState.RUNNING;

static <M, E, F> MobiusLoop<M, E, F> create(
Update<M, E, F> update,
Expand Down Expand Up @@ -146,12 +156,18 @@ public void accept(E event) {

@Override
public void dispatchEvent(E event) {
if (disposed)
if (runState == RunState.DISPOSED) {
throw new IllegalStateException(
String.format(
"This loop has already been disposed. You cannot dispatch events after "
+ "disposal - event received: %s=%s, currentModel: %s",
event.getClass().getName(), event, mostRecentModel));
}

if (runState == RunState.DISPOSING) {
// ignore events received while disposing to avoid races during shutdown
return;
}

try {
eventDispatcher.accept(checkNotNull(event));
Expand All @@ -168,9 +184,15 @@ public M getMostRecentModel() {

@Override
public Disposable observe(final Consumer<M> observer) {
if (disposed)
if (runState == RunState.DISPOSED) {
throw new IllegalStateException(
"This loop has already been disposed. You cannot observe a disposed loop");
}

if (runState == RunState.DISPOSING) {
// ignore observation requests on a disposing loop
return () -> {};
}

modelObservers.add(checkNotNull(observer));

Expand All @@ -190,6 +212,12 @@ public void dispose() {

@Override
public synchronized void dispose() {
if (runState == RunState.DISPOSED) {
return;
}

runState = RunState.DISPOSING;

// Remove model observers so that they receive no further model changes.
modelObservers.clear();

Expand All @@ -206,7 +234,7 @@ public synchronized void dispose() {
eventDispatcher.dispose();
effectDispatcher.dispose();

disposed = true;
runState = RunState.DISPOSED;
}

/**
Expand Down

0 comments on commit eb7f0e0

Please sign in to comment.