package org.multiverse.api.blocking;
import org.multiverse.api.exceptions.RetryInterruptedException;
import static java.lang.String.format;
/**
* A Cheap {@link RetryLatch} implementation based on the intrinsic lock.
*
* @author Peter Veentjer
*/
@SuppressWarnings({"SynchronizeOnThis", "WaitOrAwaitWithoutTimeout"})
public final class DefaultRetryLatch implements RetryLatch {
private volatile long era = Long.MIN_VALUE;
private volatile boolean isOpen = false;
@Override
public void open(final long expectedEra) {
if (isOpen || expectedEra != era) {
return;
}
synchronized (this) {
if (isOpen || expectedEra != era) {
return;
}
isOpen = true;
notifyAll();
}
}
@Override
public void await(long expectedEra, String transactionFamilyName) {
if (isOpen || expectedEra != era) {
return;
}
try {
synchronized (this) {
while (!isOpen && era == expectedEra) {
wait();
}
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new RetryInterruptedException(
format("[%s] Was interrupted while waiting on the retry", transactionFamilyName), ex);
}
}
@Override
public void awaitUninterruptible(final long expectedEra) {
if (isOpen || expectedEra != era) {
return;
}
boolean restoreInterrupt = false;
synchronized (this) {
while (!isOpen && era == expectedEra) {
try {
wait();
} catch (InterruptedException e) {
restoreInterrupt = true;
}
}
}
if (restoreInterrupt) {
Thread.currentThread().interrupt();
}
}
@Override
public long awaitNanosUninterruptible(final long expectedEra, long nanosTimeout) {
if (isOpen || expectedEra != era) {
return nanosTimeout;
}
if (nanosTimeout <= 0) {
return -1;
}
boolean restoreInterrupt = false;
try {
while (true) {
long startNs = System.nanoTime();
try {
synchronized (this) {
while (!isOpen && expectedEra == era) {
if (nanosTimeout <= 0) {
return -1;
}
long ms = nanosTimeout / 1000000;
int ns = (int) (nanosTimeout % 1000000);
wait(ms, ns);
nanosTimeout -= System.nanoTime() - startNs;
}
return nanosTimeout;
}
} catch (InterruptedException ex) {
restoreInterrupt = true;
nanosTimeout -= System.nanoTime() - startNs;
}
}
} finally {
if (restoreInterrupt) {
Thread.currentThread().interrupt();
}
}
}
@Override
public long awaitNanos(final long expectedEra, long nanosTimeout, String transactionFamilyName) {
if (isOpen || expectedEra != era) {
return nanosTimeout;
}
if (nanosTimeout <= 0) {
return -1;
}
try {
synchronized (this) {
while (!isOpen && expectedEra == era) {
if (nanosTimeout <= 0) {
return -1;
}
long ms = nanosTimeout / 1000000;
int ns = (int) (nanosTimeout % 1000000);
long startNs = System.nanoTime();
wait(ms, ns);
nanosTimeout -= System.nanoTime() - startNs;
}
return nanosTimeout;
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new RetryInterruptedException(
format("[%s] Was interrupted while waiting on the retry", transactionFamilyName), ex);
}
}
@Override
public long getEra() {
return era;
}
@Override
public void reset() {
synchronized (this) {
if (!isOpen) {
notifyAll();
} else {
isOpen = false;
}
era++;
}
}
@Override
public boolean isOpen() {
return isOpen;
}
@Override
public String toString() {
return format("DefaultRetryLatch(open=%s, era=%s)", isOpen, era);
}
}