Search in sources :

Example 11 with Cancellable

use of com.linkedin.r2.util.Cancellable in project rest.li by linkedin.

the class TestAsyncSharedPoolImpl method testGetItemCancelled.

@Test
public void testGetItemCancelled() throws Exception {
    final LifecycleMock lifecycle = new LifecycleMock();
    final CountDownLatch createLatch = new CountDownLatch(1);
    lifecycle.setCreateConsumer(callback -> {
        try {
            createLatch.await();
            callback.onSuccess(ITEM);
        } catch (Exception e) {
            callback.onError(e);
        }
    });
    AsyncSharedPoolImpl<Object> pool = new AsyncSharedPoolImpl<>(POOL_NAME, lifecycle, SCHEDULER, LIMITER, NO_POOL_TIMEOUT, MAX_WAITERS);
    pool.start();
    // Only one thread will perform the actual item creation task and the rest
    // will return immediately. Therefore we wait for GET_COUNT - 1 threads to complete.
    final CountDownLatch getLatch = new CountDownLatch(GET_COUNT - 1);
    final ConcurrentLinkedQueue<Cancellable> cancellables = new ConcurrentLinkedQueue<>();
    for (int i = 0; i < GET_COUNT; i++) {
        SCHEDULER.execute(() -> {
            cancellables.add(pool.get(new FutureCallback<>()));
            getLatch.countDown();
        });
    }
    if (!getLatch.await(GET_TIMEOUT, TIME_UNIT)) {
        Assert.fail("Timed out awaiting for get");
    }
    Assert.assertEquals(cancellables.size(), GET_COUNT - 1);
    // Cancelling waiters should all succeed
    cancellables.stream().forEach(cancellable -> Assert.assertTrue(cancellable.cancel()));
    // Cancel the last waiter blocking item creation
    Assert.assertEquals(pool.cancelWaiters().size(), 1);
    createLatch.countDown();
    FutureCallback<None> shutdownCallback = new FutureCallback<>();
    pool.shutdown(shutdownCallback);
    shutdownCallback.get(SHUTDOWN_TIMEOUT, TIME_UNIT);
}
Also used : Cancellable(com.linkedin.r2.util.Cancellable) CountDownLatch(java.util.concurrent.CountDownLatch) ExecutionException(java.util.concurrent.ExecutionException) AsyncSharedPoolImpl(com.linkedin.r2.transport.http.client.AsyncSharedPoolImpl) ConcurrentLinkedQueue(java.util.concurrent.ConcurrentLinkedQueue) None(com.linkedin.common.util.None) FutureCallback(com.linkedin.common.callback.FutureCallback) Test(org.testng.annotations.Test)

Example 12 with Cancellable

use of com.linkedin.r2.util.Cancellable in project rest.li by linkedin.

the class AsyncPoolImpl method get.

@Override
public Cancellable get(final Callback<T> callback) {
    // getter needs to add to wait queue atomically with check for empty pool
    // putter needs to add to pool atomically with check for empty wait queue
    boolean create = false;
    boolean reject = false;
    final LinkedDeque.Node<Callback<T>> node;
    final Callback<T> callbackWithTracking = new TimeTrackingCallback<T>(callback);
    for (; ; ) {
        TimedObject<T> obj = null;
        final State state;
        synchronized (_lock) {
            state = _state;
            if (state == State.RUNNING) {
                if (_strategy == Strategy.LRU) {
                    obj = _idle.pollFirst();
                } else {
                    obj = _idle.pollLast();
                }
                if (obj == null) {
                    if (_waiters.size() < _maxWaiters) {
                        // No objects available and the waiter list is not full; add to waiter list and break out of loop
                        node = _waiters.addLastNode(callbackWithTracking);
                        create = shouldCreate();
                    } else {
                        reject = true;
                        node = null;
                    }
                    break;
                }
            }
        }
        if (state != State.RUNNING) {
            // Defer execution of the callback until we are out of the synchronized block
            callbackWithTracking.onError(new IllegalStateException(_poolName + " is " + _state));
            return () -> false;
        }
        T rawObj = obj.get();
        if (_lifecycle.validateGet(rawObj)) {
            trc("dequeued an idle object");
            // Valid object; done
            synchronized (_lock) {
                _checkedOut++;
                _statsTracker.sampleMaxCheckedOut();
            }
            callbackWithTracking.onSuccess(rawObj);
            return () -> false;
        }
        // Invalid object, discard it and keep trying
        destroy(rawObj, true);
        trc("dequeued and disposed an invalid idle object");
    }
    if (reject) {
        // This is a recoverable exception. User can simply retry the failed get() operation.
        callbackWithTracking.onError(new SizeLimitExceededException("AsyncPool " + _poolName + " reached maximum waiter size: " + _maxWaiters));
        return null;
    }
    trc("enqueued a waiter");
    if (create) {
        create();
    }
    return new Cancellable() {

        @Override
        public boolean cancel() {
            synchronized (_lock) {
                return _waiters.removeNode(node) != null;
            }
        }
    };
}
Also used : SizeLimitExceededException(com.linkedin.r2.SizeLimitExceededException) SimpleCallback(com.linkedin.common.callback.SimpleCallback) Callback(com.linkedin.common.callback.Callback) LinkedDeque(com.linkedin.r2.util.LinkedDeque) Cancellable(com.linkedin.r2.util.Cancellable)

Example 13 with Cancellable

use of com.linkedin.r2.util.Cancellable in project rest.li by linkedin.

the class AsyncSharedPoolImpl method get.

@Override
public Cancellable get(Callback<T> callback) {
    ArgumentUtil.notNull(callback, "callback");
    final TimeTrackingCallback timeTrackingCallback = new TimeTrackingCallback(callback);
    final LinkedDeque.Node<Callback<T>> node;
    T item = null;
    boolean create = false;
    while (true) {
        final State state;
        synchronized (_lock) {
            state = _state;
            if (state == State.RUNNING) {
                item = _item.get();
                if (item == null) {
                    node = _waiters.size() < _maxWaiters ? _waiters.addLastNode(timeTrackingCallback) : null;
                    if (_isCreateInProgress) {
                        LOG.debug("{}: item creation is in progress", _name);
                    } else {
                        _isCreateInProgress = true;
                        create = true;
                    }
                    break;
                }
                _checkedOut++;
                _statsTracker.sampleMaxCheckedOut();
            }
        }
        if (state != State.RUNNING) {
            // Defer execution of the callback until we are out of the synchronized block
            timeTrackingCallback.onError(new IllegalStateException(_name + " is " + _state));
            return () -> false;
        }
        // through the item lifecycle before passing back to user callback
        if (_lifecycle.validateGet(item)) {
            timeTrackingCallback.onSuccess(item);
            return () -> false;
        }
        boolean disposed;
        synchronized (_lock) {
            // The connection has gone bad so we proceed to destroy it
            disposed = doDispose(item);
        }
        if (disposed) {
            doDestroy(item, BAD, () -> {
            });
        }
    }
    if (node == null) {
        // This is a recoverable exception. User can simply retry the failed get() operation.
        timeTrackingCallback.onError(new SizeLimitExceededException("AsyncPool " + _name + " reached maximum waiter size: " + _maxWaiters));
        return () -> false;
    }
    // The pool is currently empty we need to construct a new item
    if (create) {
        doCreate();
    }
    return () -> {
        synchronized (_lock) {
            return _waiters.removeNode(node) != null;
        }
    };
}
Also used : SizeLimitExceededException(com.linkedin.r2.SizeLimitExceededException) SimpleCallback(com.linkedin.common.callback.SimpleCallback) Callback(com.linkedin.common.callback.Callback) LinkedDeque(com.linkedin.r2.util.LinkedDeque)

Aggregations

Cancellable (com.linkedin.r2.util.Cancellable)12 FutureCallback (com.linkedin.common.callback.FutureCallback)8 AsyncSharedPoolImpl (com.linkedin.r2.transport.http.client.AsyncSharedPoolImpl)8 Test (org.testng.annotations.Test)8 None (com.linkedin.common.util.None)6 Channel (io.netty.channel.Channel)3 NioSocketChannel (io.netty.channel.socket.nio.NioSocketChannel)3 ArrayList (java.util.ArrayList)3 ExecutionException (java.util.concurrent.ExecutionException)3 Callback (com.linkedin.common.callback.Callback)2 SimpleCallback (com.linkedin.common.callback.SimpleCallback)2 SizeLimitExceededException (com.linkedin.r2.SizeLimitExceededException)2 LinkedDeque (com.linkedin.r2.util.LinkedDeque)2 ConcurrentLinkedQueue (java.util.concurrent.ConcurrentLinkedQueue)2 CountDownLatch (java.util.concurrent.CountDownLatch)2 RestRequest (com.linkedin.r2.message.rest.RestRequest)1 RestRequestBuilder (com.linkedin.r2.message.rest.RestRequestBuilder)1 TimeoutRunnable (com.linkedin.r2.util.TimeoutRunnable)1 InetAddress (java.net.InetAddress)1 InetSocketAddress (java.net.InetSocketAddress)1