You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
6.3 KiB
184 lines
6.3 KiB
7 years ago
|
/**
|
||
7 years ago
|
* Copyright 2018 Nikita Koksharov
|
||
7 years ago
|
*
|
||
|
* 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.
|
||
|
*/
|
||
7 years ago
|
package com.fr.third.org.redisson;
|
||
7 years ago
|
|
||
|
import java.util.Arrays;
|
||
|
import java.util.UUID;
|
||
|
import java.util.concurrent.TimeUnit;
|
||
|
|
||
7 years ago
|
import com.fr.third.org.redisson.api.RCountDownLatch;
|
||
|
import com.fr.third.org.redisson.api.RFuture;
|
||
|
import com.fr.third.org.redisson.client.codec.LongCodec;
|
||
|
import com.fr.third.org.redisson.client.protocol.RedisCommands;
|
||
|
import com.fr.third.org.redisson.pubsub.CountDownLatchPubSub;
|
||
|
import com.fr.third.org.redisson.api.RCountDownLatch;
|
||
|
import com.fr.third.org.redisson.api.RFuture;
|
||
|
import com.fr.third.org.redisson.client.codec.LongCodec;
|
||
|
import com.fr.third.org.redisson.client.protocol.RedisCommands;
|
||
|
import com.fr.third.org.redisson.command.CommandAsyncExecutor;
|
||
|
import com.fr.third.org.redisson.pubsub.CountDownLatchPubSub;
|
||
7 years ago
|
|
||
|
/**
|
||
|
* Distributed alternative to the {@link java.util.concurrent.CountDownLatch}
|
||
|
*
|
||
|
* It has a advantage over {@link java.util.concurrent.CountDownLatch} --
|
||
|
* count can be reset via {@link #trySetCount}.
|
||
|
*
|
||
|
* @author Nikita Koksharov
|
||
|
*
|
||
|
*/
|
||
|
public class RedissonCountDownLatch extends RedissonObject implements RCountDownLatch {
|
||
|
|
||
|
public static final Long zeroCountMessage = 0L;
|
||
|
public static final Long newCountMessage = 1L;
|
||
|
|
||
|
private static final CountDownLatchPubSub PUBSUB = new CountDownLatchPubSub();
|
||
|
|
||
|
private final UUID id;
|
||
|
|
||
7 years ago
|
protected RedissonCountDownLatch(CommandAsyncExecutor commandExecutor, String name) {
|
||
7 years ago
|
super(commandExecutor, name);
|
||
7 years ago
|
this.id = commandExecutor.getConnectionManager().getId();
|
||
7 years ago
|
}
|
||
|
|
||
|
public void await() throws InterruptedException {
|
||
7 years ago
|
RFuture<RedissonCountDownLatchEntry> future = subscribe();
|
||
7 years ago
|
try {
|
||
7 years ago
|
commandExecutor.syncSubscription(future);
|
||
7 years ago
|
|
||
|
while (getCount() > 0) {
|
||
|
// waiting for open state
|
||
|
RedissonCountDownLatchEntry entry = getEntry();
|
||
|
if (entry != null) {
|
||
|
entry.getLatch().await();
|
||
|
}
|
||
|
}
|
||
|
} finally {
|
||
7 years ago
|
unsubscribe(future);
|
||
7 years ago
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean await(long time, TimeUnit unit) throws InterruptedException {
|
||
7 years ago
|
long remainTime = unit.toMillis(time);
|
||
|
long current = System.currentTimeMillis();
|
||
|
RFuture<RedissonCountDownLatchEntry> promise = subscribe();
|
||
|
if (!await(promise, time, unit)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
7 years ago
|
try {
|
||
7 years ago
|
remainTime -= (System.currentTimeMillis() - current);
|
||
|
if (remainTime <= 0) {
|
||
7 years ago
|
return false;
|
||
|
}
|
||
|
|
||
|
while (getCount() > 0) {
|
||
7 years ago
|
if (remainTime <= 0) {
|
||
7 years ago
|
return false;
|
||
|
}
|
||
7 years ago
|
current = System.currentTimeMillis();
|
||
7 years ago
|
// waiting for open state
|
||
|
RedissonCountDownLatchEntry entry = getEntry();
|
||
|
if (entry != null) {
|
||
7 years ago
|
entry.getLatch().await(remainTime, TimeUnit.MILLISECONDS);
|
||
7 years ago
|
}
|
||
|
|
||
7 years ago
|
remainTime -= (System.currentTimeMillis() - current);
|
||
7 years ago
|
}
|
||
|
|
||
|
return true;
|
||
|
} finally {
|
||
|
unsubscribe(promise);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private RedissonCountDownLatchEntry getEntry() {
|
||
|
return PUBSUB.getEntry(getEntryName());
|
||
|
}
|
||
|
|
||
7 years ago
|
private RFuture<RedissonCountDownLatchEntry> subscribe() {
|
||
|
return PUBSUB.subscribe(getEntryName(), getChannelName(), commandExecutor.getConnectionManager().getSubscribeService());
|
||
7 years ago
|
}
|
||
|
|
||
7 years ago
|
private void unsubscribe(RFuture<RedissonCountDownLatchEntry> future) {
|
||
|
PUBSUB.unsubscribe(future.getNow(), getEntryName(), getChannelName(), commandExecutor.getConnectionManager().getSubscribeService());
|
||
7 years ago
|
}
|
||
|
|
||
|
@Override
|
||
|
public void countDown() {
|
||
|
get(countDownAsync());
|
||
|
}
|
||
|
|
||
|
@Override
|
||
7 years ago
|
public RFuture<Void> countDownAsync() {
|
||
7 years ago
|
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
|
||
|
"local v = redis.call('decr', KEYS[1]);" +
|
||
|
"if v <= 0 then redis.call('del', KEYS[1]) end;" +
|
||
|
"if v == 0 then redis.call('publish', KEYS[2], ARGV[1]) end;",
|
||
|
Arrays.<Object>asList(getName(), getChannelName()), zeroCountMessage);
|
||
|
}
|
||
|
|
||
|
private String getEntryName() {
|
||
|
return id + getName();
|
||
|
}
|
||
|
|
||
|
private String getChannelName() {
|
||
|
return "redisson_countdownlatch__channel__{" + getName() + "}";
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public long getCount() {
|
||
|
return get(getCountAsync());
|
||
|
}
|
||
|
|
||
|
@Override
|
||
7 years ago
|
public RFuture<Long> getCountAsync() {
|
||
7 years ago
|
return commandExecutor.writeAsync(getName(), LongCodec.INSTANCE, RedisCommands.GET_LONG, getName());
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean trySetCount(long count) {
|
||
|
return get(trySetCountAsync(count));
|
||
|
}
|
||
|
|
||
|
@Override
|
||
7 years ago
|
public RFuture<Boolean> trySetCountAsync(long count) {
|
||
7 years ago
|
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
|
||
|
"if redis.call('exists', KEYS[1]) == 0 then "
|
||
|
+ "redis.call('set', KEYS[1], ARGV[2]); "
|
||
|
+ "redis.call('publish', KEYS[2], ARGV[1]); "
|
||
|
+ "return 1 "
|
||
|
+ "else "
|
||
|
+ "return 0 "
|
||
|
+ "end",
|
||
|
Arrays.<Object>asList(getName(), getChannelName()), newCountMessage, count);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
7 years ago
|
public RFuture<Boolean> deleteAsync() {
|
||
7 years ago
|
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
|
||
|
"if redis.call('del', KEYS[1]) == 1 then "
|
||
|
+ "redis.call('publish', KEYS[2], ARGV[1]); "
|
||
|
+ "return 1 "
|
||
|
+ "else "
|
||
|
+ "return 0 "
|
||
|
+ "end",
|
||
|
Arrays.<Object>asList(getName(), getChannelName()), newCountMessage);
|
||
|
}
|
||
|
|
||
|
}
|