org.redisson.RedissonPermitExpirableSemaphore Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redisson Show documentation
Show all versions of redisson Show documentation
Redis Java client with features of In-Memory Data Grid
/**
* Copyright (c) 2013-2024 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import org.redisson.api.RFuture;
import org.redisson.api.RPermitExpirableSemaphore;
import org.redisson.client.codec.ByteArrayCodec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.misc.CompletableFutureWrapper;
import org.redisson.pubsub.SemaphorePubSub;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
/**
*
* @author Nikita Koksharov
*
*/
public class RedissonPermitExpirableSemaphore extends RedissonExpirable implements RPermitExpirableSemaphore {
private static final Logger LOGGER = LoggerFactory.getLogger(RedissonPermitExpirableSemaphore.class);
private final String channelName;
private final SemaphorePubSub semaphorePubSub;
private final String timeoutName;
private final long nonExpirableTimeout = 922337203685477L;
public RedissonPermitExpirableSemaphore(CommandAsyncExecutor commandExecutor, String name) {
super(commandExecutor, name);
this.timeoutName = suffixName(getRawName(), "timeout");
this.semaphorePubSub = commandExecutor.getConnectionManager().getSubscribeService().getSemaphorePubSub();
this.channelName = prefixName("redisson_sc", getRawName());
}
@Override
public String acquire() throws InterruptedException {
return acquire(-1, TimeUnit.MILLISECONDS);
}
@Override
public List acquire(int permits) throws InterruptedException {
return acquire(permits, -1, TimeUnit.MILLISECONDS);
}
@Override
public String acquire(long leaseTime, TimeUnit timeUnit) throws InterruptedException {
List ids = acquire(1, leaseTime, timeUnit);
return getFirstOrNull(ids);
}
@Override
public List acquire(int permits, long leaseTime, TimeUnit timeUnit) throws InterruptedException {
List ids = tryAcquire(permits, leaseTime, timeUnit);
if (!ids.isEmpty() && !hasOnlyNearestTimeout(ids)) {
return ids;
}
CompletableFuture future = subscribe();
semaphorePubSub.timeout(future);
RedissonLockEntry entry = commandExecutor.getInterrupted(future);
try {
while (true) {
Long nearestTimeout;
ids = tryAcquire(permits, leaseTime, timeUnit);
if (ids.isEmpty()) {
nearestTimeout = null;
} else if (hasOnlyNearestTimeout(ids)) {
nearestTimeout = Long.parseLong(ids.get(0).substring(1)) - System.currentTimeMillis();
} else {
return ids;
}
if (nearestTimeout != null) {
entry.getLatch().tryAcquire(permits, nearestTimeout, TimeUnit.MILLISECONDS);
} else {
entry.getLatch().acquire(permits);
}
}
} finally {
unsubscribe(entry);
}
// return get(acquireAsync(permits, leaseTime, timeUnit));
}
@Override
public RFuture acquireAsync() {
CompletionStage future = acquireAsync(1)
.thenApply(RedissonPermitExpirableSemaphore::getFirstOrNull);
return new CompletableFutureWrapper<>(future);
}
@Override
public RFuture> acquireAsync(int permits) {
return acquireAsync(permits, -1, TimeUnit.MILLISECONDS);
}
@Override
public RFuture acquireAsync(long leaseTime, TimeUnit timeUnit) {
CompletionStage future = acquireAsync(1, leaseTime, timeUnit)
.thenApply(RedissonPermitExpirableSemaphore::getFirstOrNull);
return new CompletableFutureWrapper<>(future);
}
@Override
public RFuture> acquireAsync(int permits, long leaseTime, TimeUnit timeUnit) {
long timeoutDate = calcTimeout(leaseTime, timeUnit);
RFuture> tryAcquireFuture = tryAcquireAsync(permits, timeoutDate);
CompletionStage> f = tryAcquireFuture.thenCompose(ids -> {
if (!ids.isEmpty() && !hasOnlyNearestTimeout(ids)) {
return CompletableFuture.completedFuture(ids);
}
CompletableFuture subscribeFuture = subscribe();
semaphorePubSub.timeout(subscribeFuture);
return subscribeFuture.thenCompose(res -> acquireAsync(permits, res, leaseTime, timeUnit));
});
f.whenComplete((r, e) -> {
if (f.toCompletableFuture().isCancelled()) {
tryAcquireFuture.whenComplete((ids, ex) -> {
if (!ids.isEmpty() && !hasOnlyNearestTimeout(ids)) {
releaseAsync(ids);
}
});
}
});
return new CompletableFutureWrapper<>(f);
}
private void tryAcquireAsync(AtomicLong time, int permits, RedissonLockEntry entry, CompletableFuture> result, long leaseTime, TimeUnit timeUnit) {
if (result.isDone()) {
unsubscribe(entry);
return;
}
if (time.get() <= 0) {
unsubscribe(entry);
result.complete(Collections.emptyList());
return;
}
long timeoutDate = calcTimeout(leaseTime, timeUnit);
long curr = System.currentTimeMillis();
RFuture> tryAcquireFuture = tryAcquireAsync(permits, timeoutDate);
tryAcquireFuture.whenComplete((ids, e) -> {
if (e != null) {
unsubscribe(entry);
result.completeExceptionally(e);
return;
}
Long nearestTimeout;
if (ids.isEmpty()) {
nearestTimeout = null;
} else if (hasOnlyNearestTimeout(ids)) {
nearestTimeout = Long.parseLong(ids.get(0).substring(1)) - System.currentTimeMillis();
} else {
unsubscribe(entry);
if (!result.complete(ids)) {
releaseAsync(ids);
}
return;
}
long el = System.currentTimeMillis() - curr;
time.addAndGet(-el);
if (time.get() <= 0) {
unsubscribe(entry);
result.complete(Collections.emptyList());
return;
}
// waiting for message
long current = System.currentTimeMillis();
if (entry.getLatch().tryAcquire()) {
tryAcquireAsync(time, permits, entry, result, leaseTime, timeUnit);
} else {
AtomicReference waitTimeoutFutureRef = new AtomicReference<>();
Timeout scheduledFuture;
if (nearestTimeout != null) {
scheduledFuture = getServiceManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
if (waitTimeoutFutureRef.get() != null && !waitTimeoutFutureRef.get().cancel()) {
return;
}
long elapsed = System.currentTimeMillis() - current;
time.addAndGet(-elapsed);
tryAcquireAsync(time, permits, entry, result, leaseTime, timeUnit);
}
}, nearestTimeout, TimeUnit.MILLISECONDS);
} else {
scheduledFuture = null;
}
Runnable listener = () -> {
if (waitTimeoutFutureRef.get() != null && !waitTimeoutFutureRef.get().cancel()) {
entry.getLatch().release();
return;
}
if (scheduledFuture != null && !scheduledFuture.cancel()) {
entry.getLatch().release();
return;
}
long elapsed = System.currentTimeMillis() - current;
time.addAndGet(-elapsed);
tryAcquireAsync(time, permits, entry, result, leaseTime, timeUnit);
};
entry.addListener(listener);
long t = time.get();
Timeout waitTimeoutFuture = getServiceManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
if (scheduledFuture != null && !scheduledFuture.cancel()) {
return;
}
if (entry.removeListener(listener)) {
long elapsed = System.currentTimeMillis() - current;
time.addAndGet(-elapsed);
tryAcquireAsync(time, permits, entry, result, leaseTime, timeUnit);
}
}
}, t, TimeUnit.MILLISECONDS);
waitTimeoutFutureRef.set(waitTimeoutFuture);
}
});
}
private CompletableFuture> acquireAsync(int permits, RedissonLockEntry entry, long leaseTime, TimeUnit timeUnit) {
long timeoutDate = calcTimeout(leaseTime, timeUnit);
CompletableFuture> tryAcquireFuture = tryAcquireAsync(permits, timeoutDate).toCompletableFuture();
return tryAcquireFuture.whenComplete((p, e) -> {
if (e != null) {
unsubscribe(entry);
}
}).thenCompose(ids -> {
Long nearestTimeout;
if (ids.isEmpty()) {
nearestTimeout = null;
} else if (hasOnlyNearestTimeout(ids)) {
nearestTimeout = Long.parseLong(ids.get(0).substring(1)) - System.currentTimeMillis();
} else {
unsubscribe(entry);
return CompletableFuture.completedFuture(ids);
}
if (entry.getLatch().tryAcquire(permits)) {
return acquireAsync(permits, entry, leaseTime, timeUnit);
}
CompletableFuture> res = new CompletableFuture<>();
Timeout scheduledFuture;
if (nearestTimeout != null) {
scheduledFuture = getServiceManager().newTimeout(timeout -> {
CompletableFuture> r = acquireAsync(permits, entry, leaseTime, timeUnit);
commandExecutor.transfer(r, res);
}, nearestTimeout, TimeUnit.MILLISECONDS);
} else {
scheduledFuture = null;
}
Runnable listener = () -> {
if (scheduledFuture != null && !scheduledFuture.cancel()) {
entry.getLatch().release();
return;
}
CompletableFuture> r = acquireAsync(permits, entry, leaseTime, timeUnit);
commandExecutor.transfer(r, res);
};
entry.addListener(listener);
return res;
});
}
@Override
public String tryAcquire() {
List ids = tryAcquire(1);
return getFirstOrNull(ids);
}
@Override
public List tryAcquire(int permits) {
List ids = tryAcquire(permits, -1, TimeUnit.MILLISECONDS);
if (hasOnlyNearestTimeout(ids)) {
return Collections.emptyList();
}
return ids;
}
private List tryAcquire(int permits, long leaseTime, TimeUnit timeUnit) {
long timeoutDate = calcTimeout(leaseTime, timeUnit);
return get(tryAcquireAsync(permits, timeoutDate));
}
private long calcTimeout(long leaseTime, TimeUnit timeUnit) {
if (leaseTime != -1) {
return System.currentTimeMillis() + timeUnit.toMillis(leaseTime);
}
return nonExpirableTimeout;
}
@Override
public RFuture tryAcquireAsync() {
CompletionStage future = tryAcquireAsync(1)
.thenApply(RedissonPermitExpirableSemaphore::getFirstOrNull);
return new CompletableFutureWrapper<>(future);
}
@Override
public RFuture> tryAcquireAsync(int permits) {
CompletableFuture> future = tryAcquireAsync(permits, nonExpirableTimeout).toCompletableFuture()
.thenApply(ids -> {
if (hasOnlyNearestTimeout(ids)) {
return null;
}
return ids;
});
future.whenComplete((ids, e) -> {
if (future.isCancelled() && !ids.isEmpty() && !hasOnlyNearestTimeout(ids)) {
releaseAsync(ids);
}
});
return new CompletableFutureWrapper<>(future);
}
private RFuture> tryAcquireAsync(int permits, long timeoutDate) {
if (permits < 0) {
throw new IllegalArgumentException("Permits amount can't be negative");
}
List ids = new ArrayList<>(permits);
for (int i = 0; i < permits; i++) {
ids.add(getServiceManager().generateId());
}
return getServiceManager().execute(() -> {
RFuture> future = tryAcquireAsync(ids, timeoutDate);
return commandExecutor.handleNoSync(future, () -> releaseAsync(ids));
});
}
private RFuture> tryAcquireAsync(List ids, long timeoutDate) {
List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy