// Copyright © 2011-2013, Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0
package fi.jumi.core.util.timeout;
import fi.jumi.actors.queue.*;
import javax.annotation.concurrent.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@ThreadSafe
public class InitialMessageTimeout<T> implements MessageSender<T> {
private final MessageSender<T> target;
private final MessageReceiver<T> timeoutMessages;
private final Timeout timeoutTimer;
private final AtomicReference<State> state = new AtomicReference<>(State.NO_MESSAGES_YET);
@Immutable
private enum State {
NO_MESSAGES_YET, TIMED_OUT, GOT_INITIAL_MESSAGE
}
public InitialMessageTimeout(MessageSender<T> target, MessageReceiver<T> timeoutMessages, long timeout, TimeUnit unit) {
this.target = target;
this.timeoutMessages = timeoutMessages;
this.timeoutTimer = new CommandExecutingTimeout(this::timedOut, timeout, unit);
this.timeoutTimer.start();
}
@Override
public void send(T message) {
State prev;
do {
prev = state.get();
if (prev == State.TIMED_OUT) {
return;
}
} while (prev != State.GOT_INITIAL_MESSAGE && !state.compareAndSet(prev, State.GOT_INITIAL_MESSAGE));
if (prev == State.NO_MESSAGES_YET) {
timeoutTimer.cancel(); // not really needed, but disposes of the timer thread a bit sooner
}
target.send(message);
}
public void timedOut() {
State prev;
do {
prev = state.get();
if (prev != State.NO_MESSAGES_YET) {
return;
}
} while (!state.compareAndSet(prev, State.TIMED_OUT));
copy(timeoutMessages, target);
}
private static <T> void copy(MessageReceiver<T> source, MessageSender<T> target) {
T message;
while ((message = source.poll()) != null) {
target.send(message);
}
}
}