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;
Callback<T> callbackWithTracking = new TimeTrackingCallback<>(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) {
if (isWaiterTimeoutEnabled()) {
callbackWithTracking = new WaiterTimeoutCallback(callbackWithTracking);
}
// 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 () -> false;
}
trc("enqueued a waiter");
if (create) {
create();
}
return new Cancellable() {
@Override
public boolean cancel() {
synchronized (_lock) {
boolean cancelled = _waiters.removeNode(node) != null;
if (cancelled) {
shutdownIfNeeded();
}
return cancelled;
}
}
};
}
use of com.linkedin.r2.util.Cancellable in project rest.li by linkedin.
the class TestAsyncSharedPoolImpl method testMixedPutAndDisposeItemSucceeds.
@Test
public void testMixedPutAndDisposeItemSucceeds() throws Exception {
AsyncSharedPoolImpl<Object> pool = new AsyncSharedPoolImpl<>(POOL_NAME, LIFECYCLE, SCHEDULER, LIMITER, NO_POOL_TIMEOUT, MAX_WAITERS);
pool.start();
final List<Object> items = new ArrayList<>(GET_COUNT);
for (int i = 0; i < GET_COUNT; i++) {
FutureCallback<Object> getCallback = new FutureCallback<>();
Cancellable cancellable = pool.get(getCallback);
// Operation should not be cancellable
Assert.assertNotNull(cancellable);
Assert.assertEquals(cancellable.cancel(), false);
Object item = getCallback.get(GET_TIMEOUT, TIME_UNIT);
Assert.assertNotNull(item);
items.add(item);
}
// All items should essentially be the same instance
Assert.assertEquals(items.size(), GET_COUNT);
items.stream().forEach(item -> Assert.assertSame(item, items.get(0)));
verifyStats(pool.getStats(), 1, GET_COUNT, 0, 0, 0, 0, 1, 0, 0);
FutureCallback<None> shutdownCallback = new FutureCallback<>();
pool.shutdown(shutdownCallback);
// Put items back to the pool
IntStream.range(0, GET_COUNT).forEach(i -> {
if (i % 2 == 0) {
pool.put(items.get(i));
} else {
pool.dispose(items.get(i));
}
});
shutdownCallback.get(SHUTDOWN_TIMEOUT, TIME_UNIT);
}
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);
}
Aggregations