/* * Copyright (c) 2018-Present Pivotal Software Inc, All Rights Reserved. * * 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 * * https://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 reactor.pool; import java.util.Deque; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import reactor.core.publisher.Mono; /** * This implementation is based on {@link java.util.concurrent.ConcurrentLinkedQueue} MPMC queue * for idle resources and a {@link ConcurrentLinkedDeque} for pending {@link Pool#acquire()} * Monos, used as a stack ({@link java.util.Deque#offerFirst(Object)}, * {@link Deque#pollFirst()}. This results in serving pending borrowers in LIFO order. * * See {@link SimplePool} for other characteristics of the simple pool. * * @author Simon Baslé */ final class SimpleLifoPool<POOLABLE> extends SimplePool<POOLABLE> { @SuppressWarnings("rawtypes") private static final ConcurrentLinkedDeque TERMINATED = new ConcurrentLinkedDeque(); volatile ConcurrentLinkedDeque<Borrower<POOLABLE>> pending; @SuppressWarnings("rawtypes") private static final AtomicReferenceFieldUpdater<SimpleLifoPool, ConcurrentLinkedDeque> PENDING = AtomicReferenceFieldUpdater.newUpdater( SimpleLifoPool.class, ConcurrentLinkedDeque.class, "pending"); public SimpleLifoPool(PoolConfig<POOLABLE> poolConfig) { super(poolConfig); this.pending = new ConcurrentLinkedDeque<>(); //unbounded } @Override boolean pendingOffer(Borrower<POOLABLE> pending) { int maxPending = poolConfig.maxPending(); for (;;) { int currentPending = PENDING_COUNT.get(this); if (maxPending >= 0 && currentPending == maxPending) { pending.fail(new PoolAcquirePendingLimitException(maxPending)); return false; } else if (PENDING_COUNT.compareAndSet(this, currentPending, currentPending + 1)) { this.pending.offerFirst(pending); //unbounded return true; } } } @Override Borrower<POOLABLE> pendingPoll() { ConcurrentLinkedDeque<Borrower<POOLABLE>> q = this.pending; Borrower<POOLABLE> b = q.pollFirst(); if (b != null) PENDING_COUNT.decrementAndGet(this); return b; } @Override void cancelAcquire(Borrower<POOLABLE> borrower) { if (!isDisposed()) { //ignore pool disposed ConcurrentLinkedDeque<Borrower<POOLABLE>> q = this.pending; if (q.remove(borrower)) { PENDING_COUNT.decrementAndGet(this); } } } @Override public Mono<Void> disposeLater() { return Mono.defer(() -> { @SuppressWarnings("unchecked") ConcurrentLinkedDeque<Borrower<POOLABLE>> q = PENDING.getAndSet(this, TERMINATED); if (q != TERMINATED) { Borrower<POOLABLE> p; while((p = q.pollFirst()) != null) { p.fail(new PoolShutdownException()); } @SuppressWarnings("unchecked") Queue<QueuePooledRef<POOLABLE>> e = ELEMENTS.getAndSet(this, null); if (e != null) { Mono<Void> destroyMonos = Mono.empty(); while (!e.isEmpty()) { QueuePooledRef<POOLABLE> ref = e.poll(); if (ref.markInvalidate()) { destroyMonos = destroyMonos.and(destroyPoolable(ref)); } } return destroyMonos; } } return Mono.empty(); }); } @Override public boolean isDisposed() { return PENDING.get(this) == TERMINATED; } }