org.redisson.RedissonBlockingFairQueue.java Source code

Java tutorial

Introduction

Here is the source code for org.redisson.RedissonBlockingFairQueue.java

Source

/**
 * Copyright 2016 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 java.util.Arrays;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.redisson.api.RBlockingFairQueue;
import org.redisson.api.RFuture;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.codec.StringCodec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandExecutor;
import org.redisson.misc.RPromise;
import org.redisson.pubsub.SemaphorePubSub;

import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;

/**
 * 
 * @author Nikita Koksharov
 *
 */
public class RedissonBlockingFairQueue<V> extends RedissonBlockingQueue<V> implements RBlockingFairQueue<V> {

    public static final long TIMEOUT_SECONDS = 30;

    private final UUID id;
    private final AtomicInteger instances = new AtomicInteger();
    private final SemaphorePubSub semaphorePubSub;

    protected RedissonBlockingFairQueue(CommandExecutor commandExecutor, String name,
            SemaphorePubSub semaphorePubSub, UUID id) {
        super(commandExecutor, name);
        this.semaphorePubSub = semaphorePubSub;
        this.id = id;
        instances.incrementAndGet();
    }

    protected RedissonBlockingFairQueue(Codec codec, CommandExecutor commandExecutor, String name,
            SemaphorePubSub semaphorePubSub, UUID id) {
        super(codec, commandExecutor, name);
        this.semaphorePubSub = semaphorePubSub;
        this.id = id;
        instances.incrementAndGet();
    }

    private String getIdsListName() {
        return suffixName(getName(), "list");
    }

    private String getTimeoutName() {
        return suffixName(getName(), "timeout");
    }

    private String getChannelName() {
        return suffixName(getName(), getCurrentId() + ":channel");
    }

    private RedissonLockEntry getEntry() {
        return semaphorePubSub.getEntry(getName());
    }

    private RFuture<RedissonLockEntry> subscribe() {
        return semaphorePubSub.subscribe(getName(), getChannelName(), commandExecutor.getConnectionManager());
    }

    private void unsubscribe(RFuture<RedissonLockEntry> future) {
        semaphorePubSub.unsubscribe(future.getNow(), getName(), getChannelName(),
                commandExecutor.getConnectionManager());
    }

    @Override
    public RFuture<Boolean> deleteAsync() {
        return commandExecutor.writeAsync(getName(), RedisCommands.DEL_OBJECTS, getName(), getIdsListName(),
                getTimeoutName());
    }

    private Long tryAcquire() {
        return get(tryAcquireAsync());
    }

    private RFuture<Long> tryAcquireAsync() {
        long timeout = System.currentTimeMillis() + TIMEOUT_SECONDS * 1000;

        return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_LONG,

                "local timeout = redis.call('get', KEYS[3]);"
                        + "if timeout ~= false and tonumber(timeout) <= tonumber(ARGV[3]) then "
                        + "redis.call('lpop', KEYS[2]); " + "local nextValue = redis.call('lindex', KEYS[2], 0); "
                        + "if nextValue ~= false and nextValue ~= ARGV[1] then "
                        + "redis.call('set', KEYS[3], ARGV[2]);"
                        + "redis.call('publish', '{' .. KEYS[1] .. '}:' .. nextValue .. ':channel', 1);" + "end; "
                        + "end; "

                        + "local items = redis.call('lrange', KEYS[2], 0, -1) " + "local found = false; "
                        + "for i=1,#items do " + "if items[i] == ARGV[1] then " + "found = true; " + "break;"
                        + "end; " + "end; "

                        + "if found == false then " + "redis.call('lpush', KEYS[2], ARGV[1]); " + "end; "

                        + "local value = redis.call('lindex', KEYS[2], 0); " + "if value == ARGV[1] then "
                        + "redis.call('set', KEYS[3], ARGV[2]);" + "local size = redis.call('llen', KEYS[2]); "
                        + "if size > 1 then " + "redis.call('lpop', KEYS[2]);"
                        + "redis.call('rpush', KEYS[2], value);"
                        + "local nextValue = redis.call('lindex', KEYS[2], 0); "
                        + "redis.call('publish', '{' .. KEYS[1] .. '}:' .. nextValue .. ':channel', 1);" + "end; "
                        + "return nil;" + "end;" + "return tonumber(timeout) - tonumber(ARGV[3]);",
                Arrays.<Object>asList(getName(), getIdsListName(), getTimeoutName()), getCurrentId(), timeout,
                System.currentTimeMillis());
    }

    private String getCurrentId() {
        return id.toString();
    }

    @Override
    public V take() throws InterruptedException {
        Long currentTimeout = tryAcquire();
        if (currentTimeout == null) {
            return super.take();
        }

        RFuture<RedissonLockEntry> future = subscribe();
        commandExecutor.syncSubscription(future);
        try {
            while (true) {
                currentTimeout = tryAcquire();
                if (currentTimeout == null) {
                    return super.take();
                }

                getEntry().getLatch().tryAcquire(currentTimeout, TimeUnit.MILLISECONDS);
            }
        } finally {
            unsubscribe(future);
        }
    }

    @Override
    public void destroy() {
        if (instances.decrementAndGet() == 0) {
            get(commandExecutor.evalWriteAsync(getName(), StringCodec.INSTANCE, RedisCommands.EVAL_VOID_WITH_VALUES,
                    "for i = 1, #ARGV, 1 do " + "redis.call('lrem', KEYS[1], 0, ARGV[i]);" + "end; ",
                    Collections.<Object>singletonList(getIdsListName()), getCurrentId()));
        }
    }

    @Override
    public RFuture<V> takeAsync() {
        final RPromise<V> promise = newPromise();

        RFuture<Long> tryAcquireFuture = tryAcquireAsync();
        tryAcquireFuture.addListener(new FutureListener<Long>() {
            @Override
            public void operationComplete(Future<Long> future) throws Exception {
                if (!future.isSuccess()) {
                    promise.tryFailure(future.cause());
                    return;
                }

                final Long currentTimeout = future.getNow();
                if (currentTimeout == null) {
                    final RFuture<V> pollFuture = RedissonBlockingFairQueue.super.takeAsync();
                    pollFuture.addListener(new FutureListener<V>() {
                        @Override
                        public void operationComplete(Future<V> future) throws Exception {
                            if (!future.isSuccess()) {
                                promise.tryFailure(future.cause());
                                return;
                            }

                            promise.trySuccess(future.getNow());
                        }
                    });
                } else {
                    final RFuture<RedissonLockEntry> subscribeFuture = subscribe();
                    final AtomicReference<Timeout> futureRef = new AtomicReference<Timeout>();
                    subscribeFuture.addListener(new FutureListener<RedissonLockEntry>() {
                        @Override
                        public void operationComplete(Future<RedissonLockEntry> future) throws Exception {
                            if (!future.isSuccess()) {
                                promise.tryFailure(future.cause());
                                return;
                            }

                            if (futureRef.get() != null) {
                                futureRef.get().cancel();
                            }

                            tryTakeAsync(subscribeFuture, promise);
                        }
                    });
                }
            }
        });

        return promise;
    }

    @Override
    public V poll() {
        Long currentTimeout = tryAcquire();
        if (currentTimeout == null) {
            return super.poll();
        }

        return null;
    }

    @Override
    public RFuture<V> pollAsync() {
        final RPromise<V> promise = newPromise();

        RFuture<Long> tryAcquireFuture = tryAcquireAsync();
        tryAcquireFuture.addListener(new FutureListener<Long>() {
            @Override
            public void operationComplete(Future<Long> future) throws Exception {
                if (!future.isSuccess()) {
                    promise.tryFailure(future.cause());
                    return;
                }

                final Long currentTimeout = future.getNow();
                if (currentTimeout == null) {
                    final RFuture<V> pollFuture = RedissonBlockingFairQueue.super.pollAsync();
                    pollFuture.addListener(new FutureListener<V>() {
                        @Override
                        public void operationComplete(Future<V> future) throws Exception {
                            if (!future.isSuccess()) {
                                promise.tryFailure(future.cause());
                                return;
                            }

                            promise.trySuccess(future.getNow());
                        }
                    });
                } else {
                    promise.trySuccess(null);
                }
            }
        });

        return promise;
    }

    @Override
    public V poll(long timeout, TimeUnit unit) throws InterruptedException {
        long startTime = System.currentTimeMillis();
        Long currentTimeout = tryAcquire();
        if (currentTimeout == null) {
            long spentTime = System.currentTimeMillis() - startTime;
            long remainTime = unit.toMillis(timeout) - spentTime;
            if (remainTime > 0) {
                return super.poll(remainTime, TimeUnit.MILLISECONDS);
            }
            return null;
        }

        RFuture<RedissonLockEntry> future = subscribe();
        long spentTime = System.currentTimeMillis() - startTime;
        long remainTime = unit.toMillis(timeout) - spentTime;
        if (!future.awaitUninterruptibly(remainTime, TimeUnit.MILLISECONDS)) {
            return null;
        }

        try {
            while (true) {
                currentTimeout = tryAcquire();
                if (currentTimeout == null) {
                    spentTime = System.currentTimeMillis() - startTime;
                    remainTime = unit.toMillis(timeout) - spentTime;
                    if (remainTime > 0) {
                        return super.poll(remainTime, TimeUnit.MILLISECONDS);
                    }
                    return null;
                }

                spentTime = System.currentTimeMillis() - startTime;
                remainTime = unit.toMillis(timeout) - spentTime;
                remainTime = Math.min(remainTime, currentTimeout);
                if (remainTime <= 0 || !getEntry().getLatch().tryAcquire(remainTime, TimeUnit.MILLISECONDS)) {
                    return null;
                }
            }
        } finally {
            unsubscribe(future);
        }
    }

    @Override
    public RFuture<V> pollAsync(final long timeout, final TimeUnit unit) {
        final long startTime = System.currentTimeMillis();
        final RPromise<V> promise = newPromise();

        RFuture<Long> tryAcquireFuture = tryAcquireAsync();
        tryAcquireFuture.addListener(new FutureListener<Long>() {
            @Override
            public void operationComplete(Future<Long> future) throws Exception {
                if (!future.isSuccess()) {
                    promise.tryFailure(future.cause());
                    return;
                }

                Long currentTimeout = future.getNow();
                if (currentTimeout == null) {
                    long spentTime = System.currentTimeMillis() - startTime;
                    long remainTime = unit.toMillis(timeout) - spentTime;
                    if (remainTime > 0) {
                        final RFuture<V> pollFuture = RedissonBlockingFairQueue.super.pollAsync(remainTime,
                                TimeUnit.MILLISECONDS);
                        pollFuture.addListener(new FutureListener<V>() {
                            @Override
                            public void operationComplete(Future<V> future) throws Exception {
                                if (!future.isSuccess()) {
                                    promise.tryFailure(future.cause());
                                    return;
                                }

                                promise.trySuccess(future.getNow());
                            }
                        });
                    } else {
                        promise.trySuccess(null);
                    }
                } else {
                    long spentTime = System.currentTimeMillis() - startTime;
                    long remainTime = unit.toMillis(timeout) - spentTime;
                    remainTime = Math.min(remainTime, currentTimeout);
                    if (remainTime <= 0) {
                        promise.trySuccess(null);
                        return;
                    }

                    final RFuture<RedissonLockEntry> subscribeFuture = subscribe();
                    final AtomicReference<Timeout> futureRef = new AtomicReference<Timeout>();
                    subscribeFuture.addListener(new FutureListener<RedissonLockEntry>() {
                        @Override
                        public void operationComplete(Future<RedissonLockEntry> future) throws Exception {
                            if (!future.isSuccess()) {
                                promise.tryFailure(future.cause());
                                return;
                            }

                            if (futureRef.get() != null) {
                                futureRef.get().cancel();
                            }

                            tryPollAsync(startTime, timeout, unit, subscribeFuture, promise);
                        }
                    });
                    if (!subscribeFuture.isDone()) {
                        Timeout scheduledFuture = commandExecutor.getConnectionManager()
                                .newTimeout(new TimerTask() {
                                    @Override
                                    public void run(Timeout timeout) throws Exception {
                                        if (!subscribeFuture.isDone()) {
                                            subscribeFuture.cancel(false);
                                            promise.trySuccess(null);
                                        }
                                    }
                                }, remainTime, TimeUnit.MILLISECONDS);
                        futureRef.set(scheduledFuture);
                    }
                }
            }
        });

        return promise;
    }

    private void tryTakeAsync(final RFuture<RedissonLockEntry> subscribeFuture, final RPromise<V> promise) {
        if (promise.isDone()) {
            unsubscribe(subscribeFuture);
            return;
        }

        RFuture<Long> tryAcquireFuture = tryAcquireAsync();
        tryAcquireFuture.addListener(new FutureListener<Long>() {
            @Override
            public void operationComplete(Future<Long> future) throws Exception {
                if (!future.isSuccess()) {
                    unsubscribe(subscribeFuture);
                    promise.tryFailure(future.cause());
                    return;
                }

                Long currentTimeout = future.getNow();
                if (currentTimeout == null) {
                    final RFuture<V> pollFuture = RedissonBlockingFairQueue.super.takeAsync();
                    pollFuture.addListener(new FutureListener<V>() {
                        @Override
                        public void operationComplete(Future<V> future) throws Exception {
                            unsubscribe(subscribeFuture);
                            if (!future.isSuccess()) {
                                promise.tryFailure(future.cause());
                                return;
                            }

                            promise.trySuccess(future.getNow());
                        }
                    });
                } else {
                    final RedissonLockEntry entry = getEntry();
                    synchronized (entry) {
                        if (entry.getLatch().tryAcquire()) {
                            tryTakeAsync(subscribeFuture, promise);
                        } else {
                            final AtomicBoolean executed = new AtomicBoolean();
                            final AtomicReference<Timeout> futureRef = new AtomicReference<Timeout>();
                            final Runnable listener = new Runnable() {
                                @Override
                                public void run() {
                                    executed.set(true);
                                    if (futureRef.get() != null) {
                                        futureRef.get().cancel();
                                    }

                                    tryTakeAsync(subscribeFuture, promise);
                                }
                            };
                            entry.addListener(listener);

                            if (!executed.get()) {
                                Timeout scheduledFuture = commandExecutor.getConnectionManager()
                                        .newTimeout(new TimerTask() {
                                            @Override
                                            public void run(Timeout t) throws Exception {
                                                synchronized (entry) {
                                                    if (entry.removeListener(listener)) {
                                                        tryTakeAsync(subscribeFuture, promise);
                                                    }
                                                }
                                            }
                                        }, currentTimeout, TimeUnit.MILLISECONDS);
                                futureRef.set(scheduledFuture);
                            }
                        }
                    }
                }
            };
        });
    }

    private void tryPollAsync(final long startTime, final long timeout, final TimeUnit unit,
            final RFuture<RedissonLockEntry> subscribeFuture, final RPromise<V> promise) {
        if (promise.isDone()) {
            unsubscribe(subscribeFuture);
            return;
        }

        long spentTime = System.currentTimeMillis() - startTime;
        long remainTime = unit.toMillis(timeout) - spentTime;
        if (remainTime <= 0) {
            unsubscribe(subscribeFuture);
            promise.trySuccess(null);
            return;
        }

        RFuture<Long> tryAcquireFuture = tryAcquireAsync();
        tryAcquireFuture.addListener(new FutureListener<Long>() {
            @Override
            public void operationComplete(Future<Long> future) throws Exception {
                if (!future.isSuccess()) {
                    unsubscribe(subscribeFuture);
                    promise.tryFailure(future.cause());
                    return;
                }

                Long currentTimeout = future.getNow();
                if (currentTimeout == null) {
                    long spentTime = System.currentTimeMillis() - startTime;
                    long remainTime = unit.toMillis(timeout) - spentTime;
                    if (remainTime > 0) {
                        final RFuture<V> pollFuture = RedissonBlockingFairQueue.super.pollAsync(remainTime,
                                TimeUnit.MILLISECONDS);
                        pollFuture.addListener(new FutureListener<V>() {
                            @Override
                            public void operationComplete(Future<V> future) throws Exception {
                                unsubscribe(subscribeFuture);
                                if (!future.isSuccess()) {
                                    promise.tryFailure(future.cause());
                                    return;
                                }

                                promise.trySuccess(future.getNow());
                            }
                        });
                    } else {
                        unsubscribe(subscribeFuture);
                        promise.trySuccess(null);
                    }
                } else {
                    final RedissonLockEntry entry = getEntry();
                    synchronized (entry) {
                        if (entry.getLatch().tryAcquire()) {
                            tryPollAsync(startTime, timeout, unit, subscribeFuture, promise);
                        } else {
                            final AtomicBoolean executed = new AtomicBoolean();
                            final AtomicReference<Timeout> futureRef = new AtomicReference<Timeout>();
                            final Runnable listener = new Runnable() {
                                @Override
                                public void run() {
                                    executed.set(true);
                                    if (futureRef.get() != null) {
                                        futureRef.get().cancel();
                                    }

                                    tryPollAsync(startTime, timeout, unit, subscribeFuture, promise);
                                }
                            };
                            entry.addListener(listener);

                            if (!executed.get()) {
                                long spentTime = System.currentTimeMillis() - startTime;
                                long remainTime = unit.toMillis(timeout) - spentTime;
                                Timeout scheduledFuture = commandExecutor.getConnectionManager()
                                        .newTimeout(new TimerTask() {
                                            @Override
                                            public void run(Timeout t) throws Exception {
                                                synchronized (entry) {
                                                    if (entry.removeListener(listener)) {
                                                        tryPollAsync(startTime, timeout, unit, subscribeFuture,
                                                                promise);
                                                    }
                                                }
                                            }
                                        }, remainTime, TimeUnit.MILLISECONDS);
                                futureRef.set(scheduledFuture);
                            }
                        }
                    }
                }
            };
        });
    }

    @Override
    public V pollLastAndOfferFirstTo(String queueName, long timeout, TimeUnit unit) throws InterruptedException {
        long startTime = System.currentTimeMillis();
        Long currentTimeout = tryAcquire();
        if (currentTimeout == null) {
            long spentTime = System.currentTimeMillis() - startTime;
            long remainTime = unit.toMillis(timeout) - spentTime;
            if (remainTime > 0) {
                return super.pollLastAndOfferFirstTo(queueName, remainTime, TimeUnit.MILLISECONDS);
            }
            return null;
        }

        RFuture<RedissonLockEntry> future = subscribe();
        long spentTime = System.currentTimeMillis() - startTime;
        long remainTime = unit.toMillis(timeout) - spentTime;
        if (!future.awaitUninterruptibly(remainTime, TimeUnit.MILLISECONDS)) {
            return null;
        }

        try {
            while (true) {
                currentTimeout = tryAcquire();
                if (currentTimeout == null) {
                    spentTime = System.currentTimeMillis() - startTime;
                    remainTime = unit.toMillis(timeout) - spentTime;
                    if (remainTime > 0) {
                        return super.pollLastAndOfferFirstTo(queueName, remainTime, TimeUnit.MILLISECONDS);
                    }
                    return null;
                }

                spentTime = System.currentTimeMillis() - startTime;
                remainTime = unit.toMillis(timeout) - spentTime;
                remainTime = Math.min(remainTime, currentTimeout);
                if (remainTime <= 0 || !getEntry().getLatch().tryAcquire(remainTime, TimeUnit.MILLISECONDS)) {
                    return null;
                }
            }
        } finally {
            unsubscribe(future);
        }
    }

    @Override
    public RFuture<V> pollLastAndOfferFirstToAsync(final String queueName, final long timeout,
            final TimeUnit unit) {
        final long startTime = System.currentTimeMillis();
        final RPromise<V> promise = newPromise();

        RFuture<Long> tryAcquireFuture = tryAcquireAsync();
        tryAcquireFuture.addListener(new FutureListener<Long>() {
            @Override
            public void operationComplete(Future<Long> future) throws Exception {
                if (!future.isSuccess()) {
                    promise.tryFailure(future.cause());
                    return;
                }

                Long currentTimeout = future.getNow();
                if (currentTimeout == null) {
                    long spentTime = System.currentTimeMillis() - startTime;
                    long remainTime = unit.toMillis(timeout) - spentTime;
                    if (remainTime > 0) {
                        final RFuture<V> pollFuture = RedissonBlockingFairQueue.super.pollLastAndOfferFirstToAsync(
                                queueName, remainTime, TimeUnit.MILLISECONDS);
                        pollFuture.addListener(new FutureListener<V>() {
                            @Override
                            public void operationComplete(Future<V> future) throws Exception {
                                if (!future.isSuccess()) {
                                    promise.tryFailure(future.cause());
                                    return;
                                }

                                promise.trySuccess(future.getNow());
                            }
                        });
                    } else {
                        promise.trySuccess(null);
                    }
                } else {
                    long spentTime = System.currentTimeMillis() - startTime;
                    long remainTime = unit.toMillis(timeout) - spentTime;
                    remainTime = Math.min(remainTime, currentTimeout);
                    if (remainTime <= 0) {
                        promise.trySuccess(null);
                        return;
                    }

                    final RFuture<RedissonLockEntry> subscribeFuture = subscribe();
                    final AtomicReference<Timeout> futureRef = new AtomicReference<Timeout>();
                    subscribeFuture.addListener(new FutureListener<RedissonLockEntry>() {
                        @Override
                        public void operationComplete(Future<RedissonLockEntry> future) throws Exception {
                            if (!future.isSuccess()) {
                                promise.tryFailure(future.cause());
                                return;
                            }

                            if (futureRef.get() != null) {
                                futureRef.get().cancel();
                            }

                            tryPollLastAndOfferFirstToAsync(startTime, timeout, unit, subscribeFuture, promise,
                                    queueName);
                        }
                    });
                    if (!subscribeFuture.isDone()) {
                        Timeout scheduledFuture = commandExecutor.getConnectionManager()
                                .newTimeout(new TimerTask() {
                                    @Override
                                    public void run(Timeout timeout) throws Exception {
                                        if (!subscribeFuture.isDone()) {
                                            subscribeFuture.cancel(false);
                                            promise.trySuccess(null);
                                        }
                                    }
                                }, remainTime, TimeUnit.MILLISECONDS);
                        futureRef.set(scheduledFuture);
                    }
                }
            }
        });

        return promise;
    }

    private void tryPollLastAndOfferFirstToAsync(final long startTime, final long timeout, final TimeUnit unit,
            final RFuture<RedissonLockEntry> subscribeFuture, final RPromise<V> promise, final String queueName) {
        if (promise.isDone()) {
            unsubscribe(subscribeFuture);
            return;
        }

        long spentTime = System.currentTimeMillis() - startTime;
        long remainTime = unit.toMillis(timeout) - spentTime;
        if (remainTime <= 0) {
            unsubscribe(subscribeFuture);
            promise.trySuccess(null);
            return;
        }

        RFuture<Long> tryAcquireFuture = tryAcquireAsync();
        tryAcquireFuture.addListener(new FutureListener<Long>() {
            @Override
            public void operationComplete(Future<Long> future) throws Exception {
                if (!future.isSuccess()) {
                    unsubscribe(subscribeFuture);
                    promise.tryFailure(future.cause());
                    return;
                }

                Long currentTimeout = future.getNow();
                if (currentTimeout == null) {
                    long spentTime = System.currentTimeMillis() - startTime;
                    long remainTime = unit.toMillis(timeout) - spentTime;
                    if (remainTime > 0) {
                        final RFuture<V> pollFuture = RedissonBlockingFairQueue.super.pollLastAndOfferFirstToAsync(
                                queueName, remainTime, TimeUnit.MILLISECONDS);
                        pollFuture.addListener(new FutureListener<V>() {
                            @Override
                            public void operationComplete(Future<V> future) throws Exception {
                                unsubscribe(subscribeFuture);
                                if (!future.isSuccess()) {
                                    promise.tryFailure(future.cause());
                                    return;
                                }

                                promise.trySuccess(future.getNow());
                            }
                        });
                    } else {
                        unsubscribe(subscribeFuture);
                        promise.trySuccess(null);
                    }
                } else {
                    final RedissonLockEntry entry = getEntry();
                    synchronized (entry) {
                        if (entry.getLatch().tryAcquire()) {
                            tryPollAsync(startTime, timeout, unit, subscribeFuture, promise);
                        } else {
                            final AtomicBoolean executed = new AtomicBoolean();
                            final AtomicReference<Timeout> futureRef = new AtomicReference<Timeout>();
                            final Runnable listener = new Runnable() {
                                @Override
                                public void run() {
                                    executed.set(true);
                                    if (futureRef.get() != null) {
                                        futureRef.get().cancel();
                                    }

                                    tryPollLastAndOfferFirstToAsync(startTime, timeout, unit, subscribeFuture,
                                            promise, queueName);
                                }
                            };
                            entry.addListener(listener);

                            if (!executed.get()) {
                                long spentTime = System.currentTimeMillis() - startTime;
                                long remainTime = unit.toMillis(timeout) - spentTime;
                                Timeout scheduledFuture = commandExecutor.getConnectionManager()
                                        .newTimeout(new TimerTask() {
                                            @Override
                                            public void run(Timeout t) throws Exception {
                                                synchronized (entry) {
                                                    if (entry.removeListener(listener)) {
                                                        tryPollLastAndOfferFirstToAsync(startTime, timeout, unit,
                                                                subscribeFuture, promise, queueName);
                                                    }
                                                }
                                            }
                                        }, remainTime, TimeUnit.MILLISECONDS);
                                futureRef.set(scheduledFuture);
                            }
                        }
                    }
                }
            };
        });
    }

}