|
|
@ -15,10 +15,6 @@
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
package org.redisson.pubsub;
|
|
|
|
package org.redisson.pubsub;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
|
|
|
|
import java.util.concurrent.ConcurrentMap;
|
|
|
|
|
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import org.redisson.PubSubEntry;
|
|
|
|
import org.redisson.PubSubEntry;
|
|
|
|
import org.redisson.api.RFuture;
|
|
|
|
import org.redisson.api.RFuture;
|
|
|
|
import org.redisson.client.BaseRedisPubSubListener;
|
|
|
|
import org.redisson.client.BaseRedisPubSubListener;
|
|
|
@ -30,6 +26,9 @@ import org.redisson.misc.RPromise;
|
|
|
|
import org.redisson.misc.RedissonPromise;
|
|
|
|
import org.redisson.misc.RedissonPromise;
|
|
|
|
import org.redisson.misc.TransferListener;
|
|
|
|
import org.redisson.misc.TransferListener;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
|
|
|
|
import java.util.concurrent.ConcurrentMap;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @author Nikita Koksharov
|
|
|
|
* @author Nikita Koksharov
|
|
|
@ -37,75 +36,63 @@ import org.redisson.misc.TransferListener;
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
abstract class PublishSubscribe<E extends PubSubEntry<E>> {
|
|
|
|
abstract class PublishSubscribe<E extends PubSubEntry<E>> {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final ConcurrentMap<String, E> entries = new ConcurrentHashMap<>();
|
|
|
|
private final PublishSubscribeService service;
|
|
|
|
private final PublishSubscribeService service;
|
|
|
|
|
|
|
|
|
|
|
|
PublishSubscribe(PublishSubscribeService service) {
|
|
|
|
PublishSubscribe(PublishSubscribeService service) {
|
|
|
|
super();
|
|
|
|
super();
|
|
|
|
this.service = service;
|
|
|
|
this.service = service;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private final ConcurrentMap<String, E> entries = new ConcurrentHashMap<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void unsubscribe(E entry, String entryName, String channelName) {
|
|
|
|
public void unsubscribe(E entry, String entryName, String channelName) {
|
|
|
|
AsyncSemaphore semaphore = service.getSemaphore(new ChannelName(channelName));
|
|
|
|
AsyncSemaphore semaphore = service.getSemaphore(new ChannelName(channelName));
|
|
|
|
semaphore.acquire(new Runnable() {
|
|
|
|
semaphore.acquire(() -> {
|
|
|
|
@Override
|
|
|
|
if (entry.release() == 0) {
|
|
|
|
public void run() {
|
|
|
|
// just an assertion
|
|
|
|
if (entry.release() == 0) {
|
|
|
|
boolean removed = entries.remove(entryName) == entry;
|
|
|
|
// just an assertion
|
|
|
|
if (!removed) {
|
|
|
|
boolean removed = entries.remove(entryName) == entry;
|
|
|
|
throw new IllegalStateException();
|
|
|
|
if (!removed) {
|
|
|
|
|
|
|
|
throw new IllegalStateException();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
service.unsubscribe(new ChannelName(channelName), semaphore);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
semaphore.release();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
service.unsubscribe(new ChannelName(channelName), semaphore);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
semaphore.release();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public RFuture<E> subscribe(String entryName, String channelName) {
|
|
|
|
public RFuture<E> subscribe(String entryName, String channelName) {
|
|
|
|
AtomicReference<Runnable> listenerHolder = new AtomicReference<Runnable>();
|
|
|
|
|
|
|
|
AsyncSemaphore semaphore = service.getSemaphore(new ChannelName(channelName));
|
|
|
|
AsyncSemaphore semaphore = service.getSemaphore(new ChannelName(channelName));
|
|
|
|
RPromise<E> newPromise = new RedissonPromise<E>() {
|
|
|
|
RPromise<E> newPromise = new RedissonPromise<>();
|
|
|
|
@Override
|
|
|
|
semaphore.acquire(() -> {
|
|
|
|
public boolean cancel(boolean mayInterruptIfRunning) {
|
|
|
|
if (!newPromise.setUncancellable()) {
|
|
|
|
return semaphore.remove(listenerHolder.get());
|
|
|
|
semaphore.release();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Runnable listener = new Runnable() {
|
|
|
|
E entry = entries.get(entryName);
|
|
|
|
|
|
|
|
if (entry != null) {
|
|
|
|
|
|
|
|
entry.acquire();
|
|
|
|
|
|
|
|
semaphore.release();
|
|
|
|
|
|
|
|
entry.getPromise().onComplete(new TransferListener<E>(newPromise));
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
E value = createEntry(newPromise);
|
|
|
|
public void run() {
|
|
|
|
value.acquire();
|
|
|
|
E entry = entries.get(entryName);
|
|
|
|
|
|
|
|
if (entry != null) {
|
|
|
|
E oldValue = entries.putIfAbsent(entryName, value);
|
|
|
|
entry.acquire();
|
|
|
|
if (oldValue != null) {
|
|
|
|
semaphore.release();
|
|
|
|
oldValue.acquire();
|
|
|
|
entry.getPromise().onComplete(new TransferListener<E>(newPromise));
|
|
|
|
semaphore.release();
|
|
|
|
return;
|
|
|
|
oldValue.getPromise().onComplete(new TransferListener<E>(newPromise));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
E value = createEntry(newPromise);
|
|
|
|
|
|
|
|
value.acquire();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
E oldValue = entries.putIfAbsent(entryName, value);
|
|
|
|
|
|
|
|
if (oldValue != null) {
|
|
|
|
|
|
|
|
oldValue.acquire();
|
|
|
|
|
|
|
|
semaphore.release();
|
|
|
|
|
|
|
|
oldValue.getPromise().onComplete(new TransferListener<E>(newPromise));
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RedisPubSubListener<Object> listener = createListener(channelName, value);
|
|
|
|
|
|
|
|
service.subscribe(LongCodec.INSTANCE, channelName, semaphore, listener);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
semaphore.acquire(listener);
|
|
|
|
RedisPubSubListener<Object> listener = createListener(channelName, value);
|
|
|
|
listenerHolder.set(listener);
|
|
|
|
service.subscribe(LongCodec.INSTANCE, channelName, semaphore, listener);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return newPromise;
|
|
|
|
return newPromise;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|