This commit is contained in:
Andrea Cavalli 2021-01-22 04:22:58 +01:00
parent ff44ed16ba
commit 827bb23038
2 changed files with 30 additions and 15 deletions

View File

@ -1,25 +1,37 @@
package org.warp.commonutils.locks;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
public class TransitLock {
private final FlexibleCountDownLatch unfinishedTransits = new FlexibleCountDownLatch(0);
private final Semaphore permits = new Semaphore(Integer.MAX_VALUE);
private final AtomicInteger allowedTransits = new AtomicInteger(1);
private final Object synchronization = new Object();
public TransitLock() {
}
public void allowTransit() {
var permitsToRelease = Math.max(0, Integer.MAX_VALUE - (permits.availablePermits() + 10000));
permits.release(permitsToRelease);
var allowedTransits = this.allowedTransits.incrementAndGet();
if (allowedTransits == 1) {
var permitsToRelease = Math.max(0, Integer.MAX_VALUE - (permits.availablePermits() + 10000));
permits.release(permitsToRelease);
} else if (allowedTransits > 1) {
throw new IllegalStateException("Already allowed transit!");
}
}
public void disallowTransit() {
synchronized (synchronization) {
unfinishedTransits.await();
permits.drainPermits();
var allowedTransits = this.allowedTransits.decrementAndGet();
if (allowedTransits == 0) {
unfinishedTransits.await();
permits.drainPermits();
} else if (allowedTransits > 0) {
throw new IllegalStateException("Transits are still allowed for an unknown reason!");
}
}
}

View File

@ -33,17 +33,20 @@ public class TransitLockTest {
@Test
public void testAllowTransit() {
var lock = new TransitLock();
lock.disallowTransit();
lock.allowTransit();
lock.transit();
}
@Test
public void testMultiAllowTransit() {
var lock = new TransitLock();
lock.allowTransit();
lock.allowTransit();
lock.allowTransit();
lock.transit();
Assertions.assertThrows(IllegalStateException.class, () -> {
var lock = new TransitLock();
lock.allowTransit();
lock.allowTransit();
lock.allowTransit();
lock.transit();
});
}
@Test
@ -54,6 +57,7 @@ public class TransitLockTest {
lock.disallowTransit();
lock.disallowTransit();
lock.allowTransit();
lock.allowTransit();
lock.transit();
}
@ -86,13 +90,13 @@ public class TransitLockTest {
var lock = new TransitLock();
AtomicInteger alreadyRunningTransits = new AtomicInteger();
var pool = Executors.newFixedThreadPool(100);
AtomicReference<AssertionError> failure = new AtomicReference<>();
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 < 100; j++) {
for (int j = 0; j < 500; j++) {
if (iF % 2 == 0) {
lock.startTransit();
alreadyRunningTransits.getAndIncrement();
@ -104,15 +108,14 @@ public class TransitLockTest {
lock.allowTransit();
}
}
} catch (AssertionError e) {
} catch (Exception e) {
e.printStackTrace();
failure.set(e);
}
});
}
lock.allowTransit();
pool.shutdown();
if (failure.get() != null) throw failure.get();
if (failure.get() != null) throw new AssertionError(failure.get());
Assertions.assertDoesNotThrow(() -> pool.awaitTermination(10, TimeUnit.SECONDS));
Assertions.assertTrue(pool.isTerminated());
}