157 lines
4.0 KiB
Java
157 lines
4.0 KiB
Java
package org.warp.commonutils.locks;
|
|
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.TimeoutException;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
import org.junit.jupiter.api.Assertions;
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
public class TransitLockTest {
|
|
|
|
@Test
|
|
public void testTransit() {
|
|
var lock = new TransitLock();
|
|
lock.transit();
|
|
lock.transit();
|
|
lock.transit();
|
|
}
|
|
|
|
@Test
|
|
public void testPartialTransit() {
|
|
var lock = new TransitLock();
|
|
lock.startTransit();
|
|
lock.endTransit();
|
|
lock.startTransit();
|
|
lock.endTransit();
|
|
lock.startTransit();
|
|
lock.endTransit();
|
|
}
|
|
|
|
@Test
|
|
public void testAllowTransit() {
|
|
var lock = new TransitLock();
|
|
lock.disallowTransit();
|
|
lock.allowTransit();
|
|
lock.transit();
|
|
}
|
|
|
|
@Test
|
|
public void testMultiAllowTransit() {
|
|
Assertions.assertThrows(IllegalStateException.class, () -> {
|
|
var lock = new TransitLock();
|
|
lock.allowTransit();
|
|
lock.allowTransit();
|
|
lock.allowTransit();
|
|
lock.transit();
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testMultiDisallowAllowTransit() {
|
|
var lock = new TransitLock();
|
|
lock.disallowTransit();
|
|
lock.allowTransit();
|
|
lock.disallowTransit();
|
|
lock.disallowTransit();
|
|
lock.allowTransit();
|
|
lock.allowTransit();
|
|
lock.transit();
|
|
}
|
|
|
|
@Test
|
|
public void testDisallowTransit() {
|
|
var lock = new TransitLock();
|
|
lock.disallowTransit();
|
|
for (int i = 0; i < 10; i++) {
|
|
Assertions.assertThrows(TimeoutException.class, () -> {
|
|
CompletableFuture.runAsync(lock::transit).get(5, TimeUnit.MILLISECONDS);
|
|
}, "Disallowed transit didn't block a transit");
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testMultiDisallowTransit() {
|
|
var lock = new TransitLock();
|
|
lock.disallowTransit();
|
|
lock.disallowTransit();
|
|
lock.disallowTransit();
|
|
for (int i = 0; i < 10; i++) {
|
|
Assertions.assertThrows(TimeoutException.class, () -> {
|
|
CompletableFuture.runAsync(lock::transit).get(5, TimeUnit.MILLISECONDS);
|
|
}, "Disallowed transit didn't block a transit");
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testDeadlocks() {
|
|
var lock = new TransitLock();
|
|
AtomicInteger alreadyRunningTransits = new AtomicInteger();
|
|
|
|
var pool = Executors.newFixedThreadPool(500);
|
|
AtomicReference<Exception> failure = new AtomicReference<>();
|
|
for (int i = 0; i < 100; i++) {
|
|
int iF = i;
|
|
pool.submit(() -> {
|
|
try {
|
|
for (int j = 0; j < 500; j++) {
|
|
if (iF % 2 == 0) {
|
|
lock.startTransit();
|
|
alreadyRunningTransits.getAndIncrement();
|
|
alreadyRunningTransits.decrementAndGet();
|
|
lock.endTransit();
|
|
} else {
|
|
lock.disallowTransit();
|
|
Assertions.assertEquals(0, alreadyRunningTransits.get());
|
|
lock.allowTransit();
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
failure.set(e);
|
|
}
|
|
});
|
|
}
|
|
pool.shutdown();
|
|
if (failure.get() != null) throw new AssertionError(failure.get());
|
|
Assertions.assertDoesNotThrow(() -> pool.awaitTermination(10, TimeUnit.SECONDS));
|
|
Assertions.assertTrue(pool.isTerminated());
|
|
}
|
|
|
|
@Test
|
|
public void testParallelTransit() {
|
|
var lock = new TransitLock();
|
|
AtomicInteger alreadyRunningTransits = new AtomicInteger();
|
|
|
|
var pool = Executors.newFixedThreadPool(100);
|
|
AtomicReference<AssertionError> failure = new AtomicReference<>();
|
|
for (int i = 0; i < 100; i++) {
|
|
pool.submit(() -> {
|
|
try {
|
|
lock.startTransit();
|
|
alreadyRunningTransits.getAndIncrement();
|
|
try {
|
|
Thread.sleep(20);
|
|
} catch (InterruptedException e) {
|
|
Assertions.fail(e);
|
|
}
|
|
alreadyRunningTransits.decrementAndGet();
|
|
lock.endTransit();
|
|
} catch (AssertionError e) {
|
|
e.printStackTrace();
|
|
failure.set(e);
|
|
}
|
|
});
|
|
}
|
|
lock.disallowTransit();
|
|
Assertions.assertEquals(0, alreadyRunningTransits.get());
|
|
lock.allowTransit();
|
|
pool.shutdown();
|
|
if (failure.get() != null) throw failure.get();
|
|
Assertions.assertDoesNotThrow(() -> pool.awaitTermination(10, TimeUnit.SECONDS));
|
|
Assertions.assertTrue(pool.isTerminated());
|
|
}
|
|
}
|