112f608f3d2089439a108788a1908941eea4277b9Puneet Lall/* 212f608f3d2089439a108788a1908941eea4277b9Puneet Lall * Copyright (C) 2014 The Android Open Source Project 312f608f3d2089439a108788a1908941eea4277b9Puneet Lall * 412f608f3d2089439a108788a1908941eea4277b9Puneet Lall * Licensed under the Apache License, Version 2.0 (the "License"); 512f608f3d2089439a108788a1908941eea4277b9Puneet Lall * you may not use this file except in compliance with the License. 612f608f3d2089439a108788a1908941eea4277b9Puneet Lall * You may obtain a copy of the License at 712f608f3d2089439a108788a1908941eea4277b9Puneet Lall * 812f608f3d2089439a108788a1908941eea4277b9Puneet Lall * http://www.apache.org/licenses/LICENSE-2.0 912f608f3d2089439a108788a1908941eea4277b9Puneet Lall * 1012f608f3d2089439a108788a1908941eea4277b9Puneet Lall * Unless required by applicable law or agreed to in writing, software 1112f608f3d2089439a108788a1908941eea4277b9Puneet Lall * distributed under the License is distributed on an "AS IS" BASIS, 1212f608f3d2089439a108788a1908941eea4277b9Puneet Lall * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1312f608f3d2089439a108788a1908941eea4277b9Puneet Lall * See the License for the specific language governing permissions and 1412f608f3d2089439a108788a1908941eea4277b9Puneet Lall * limitations under the License. 1512f608f3d2089439a108788a1908941eea4277b9Puneet Lall */ 1612f608f3d2089439a108788a1908941eea4277b9Puneet Lall 1712f608f3d2089439a108788a1908941eea4277b9Puneet Lallpackage com.android.camera.one.v2.sharedimagereader.ticketpool; 1812f608f3d2089439a108788a1908941eea4277b9Puneet Lall 19e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lallimport com.android.camera.async.ConcurrentState; 20e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lallimport com.android.camera.async.Observable; 21e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lallimport com.android.camera.async.SafeCloseable; 22e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 2312f608f3d2089439a108788a1908941eea4277b9Puneet Lallimport java.util.ArrayList; 2412f608f3d2089439a108788a1908941eea4277b9Puneet Lallimport java.util.Collection; 25e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lallimport java.util.LinkedList; 2612f608f3d2089439a108788a1908941eea4277b9Puneet Lallimport java.util.List; 2712f608f3d2089439a108788a1908941eea4277b9Puneet Lallimport java.util.concurrent.atomic.AtomicBoolean; 28e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lallimport java.util.concurrent.locks.Condition; 29e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lallimport java.util.concurrent.locks.ReentrantLock; 3012f608f3d2089439a108788a1908941eea4277b9Puneet Lall 31e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lallimport javax.annotation.Nonnull; 32e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lallimport javax.annotation.concurrent.GuardedBy; 3312f608f3d2089439a108788a1908941eea4277b9Puneet Lall 3412f608f3d2089439a108788a1908941eea4277b9Puneet Lall/** 3512f608f3d2089439a108788a1908941eea4277b9Puneet Lall * A ticket pool with a finite number of tickets. 3612f608f3d2089439a108788a1908941eea4277b9Puneet Lall */ 37e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lallpublic final class FiniteTicketPool implements TicketPool, SafeCloseable { 3812f608f3d2089439a108788a1908941eea4277b9Puneet Lall private class TicketImpl implements Ticket { 3912f608f3d2089439a108788a1908941eea4277b9Puneet Lall private final AtomicBoolean mClosed; 4012f608f3d2089439a108788a1908941eea4277b9Puneet Lall 4112f608f3d2089439a108788a1908941eea4277b9Puneet Lall public TicketImpl() { 4212f608f3d2089439a108788a1908941eea4277b9Puneet Lall mClosed = new AtomicBoolean(false); 4312f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 4412f608f3d2089439a108788a1908941eea4277b9Puneet Lall 4512f608f3d2089439a108788a1908941eea4277b9Puneet Lall @Override 4612f608f3d2089439a108788a1908941eea4277b9Puneet Lall public void close() { 4712f608f3d2089439a108788a1908941eea4277b9Puneet Lall boolean alreadyClosed = mClosed.getAndSet(true); 4812f608f3d2089439a108788a1908941eea4277b9Puneet Lall if (!alreadyClosed) { 49e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall synchronized (mLock) { 50e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall releaseTicket(); 51e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall updateAvailableTicketCount(); 52e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 5312f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 5412f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 5512f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 5612f608f3d2089439a108788a1908941eea4277b9Puneet Lall 57e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall private class Waiter { 58e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall private final int mTicketsRequested; 59e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall private final Condition mCondition; 60e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 61e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall private Waiter(int ticketsRequested, Condition condition) { 62e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mTicketsRequested = ticketsRequested; 63e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mCondition = condition; 64e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 65e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 66e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall public Condition getCondition() { 67e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall return mCondition; 68e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 69e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 70e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall public int getTicketsRequested() { 71e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall return mTicketsRequested; 72e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 73e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 74e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 7512f608f3d2089439a108788a1908941eea4277b9Puneet Lall private final int mMaxCapacity; 76e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall private final ReentrantLock mLock; 77e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall @GuardedBy("mLock") 78e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall private final LinkedList<Waiter> mWaiters; 79e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall private final ConcurrentState<Integer> mAvailableTicketCount; 80e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall @GuardedBy("mLock") 81e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall private int mTickets; 82e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall @GuardedBy("mLock") 83e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall private boolean mClosed; 8412f608f3d2089439a108788a1908941eea4277b9Puneet Lall 8512f608f3d2089439a108788a1908941eea4277b9Puneet Lall public FiniteTicketPool(int capacity) { 8612f608f3d2089439a108788a1908941eea4277b9Puneet Lall mMaxCapacity = capacity; 87e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mLock = new ReentrantLock(true); 88e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mTickets = capacity; 89e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mWaiters = new LinkedList<>(); 90e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mClosed = false; 91e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mAvailableTicketCount = new ConcurrentState<>(capacity); 92e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 93e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 94e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall @GuardedBy("mLock") 95e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall private void releaseTicket() { 96e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mLock.lock(); 97e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall try { 98e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mTickets++; 99e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 100e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall // Wake up waiters in order, so long as their requested number of 101e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall // tickets can be satisfied. 102e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall int ticketsRemaining = mTickets; 103e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall Waiter nextWaiter = mWaiters.peekFirst(); 104e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall while (nextWaiter != null && nextWaiter.getTicketsRequested() <= ticketsRemaining) { 105e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall ticketsRemaining -= nextWaiter.getTicketsRequested(); 106e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall nextWaiter.getCondition().signal(); 107e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 108e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mWaiters.removeFirst(); 109e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall nextWaiter = mWaiters.peekFirst(); 110e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 111e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } finally { 112e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mLock.unlock(); 113e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 11412f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 11512f608f3d2089439a108788a1908941eea4277b9Puneet Lall 116e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall @Nonnull 11712f608f3d2089439a108788a1908941eea4277b9Puneet Lall @Override 11812f608f3d2089439a108788a1908941eea4277b9Puneet Lall public Collection<Ticket> acquire(int tickets) throws InterruptedException, 11912f608f3d2089439a108788a1908941eea4277b9Puneet Lall NoCapacityAvailableException { 120e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mLock.lock(); 121e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall try { 122e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall if (tickets > mMaxCapacity || tickets < 0) { 123e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall throw new NoCapacityAvailableException(); 124e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 125e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall Waiter thisWaiter = new Waiter(tickets, mLock.newCondition()); 126e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mWaiters.addLast(thisWaiter); 127e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall updateAvailableTicketCount(); 128e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall try { 129e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall while (mTickets < tickets && !mClosed) { 130e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall thisWaiter.getCondition().await(); 131e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 132e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall if (mClosed) { 133e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall throw new NoCapacityAvailableException(); 134e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 135e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 136e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mTickets -= tickets; 137e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 138e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall updateAvailableTicketCount(); 139e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 140e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall List<Ticket> ticketList = new ArrayList<>(); 141e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall for (int i = 0; i < tickets; i++) { 142e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall ticketList.add(new TicketImpl()); 143e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 144e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall return ticketList; 145e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } finally { 146e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mWaiters.remove(thisWaiter); 147e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall updateAvailableTicketCount(); 148e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 149e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } finally { 150e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mLock.unlock(); 15112f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 152e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 153e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 154e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall @GuardedBy("mLock") 155e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall private void updateAvailableTicketCount() { 156e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall if (mClosed || !mWaiters.isEmpty()) { 157e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mAvailableTicketCount.update(0); 158e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } else { 159e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mAvailableTicketCount.update(mTickets); 16012f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 16112f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 16212f608f3d2089439a108788a1908941eea4277b9Puneet Lall 163e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall @Nonnull 16412f608f3d2089439a108788a1908941eea4277b9Puneet Lall @Override 165e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall public Observable<Integer> getAvailableTicketCount() { 166e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall return mAvailableTicketCount; 16712f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 16812f608f3d2089439a108788a1908941eea4277b9Puneet Lall 16912f608f3d2089439a108788a1908941eea4277b9Puneet Lall @Override 17012f608f3d2089439a108788a1908941eea4277b9Puneet Lall public Ticket tryAcquire() { 171e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mLock.lock(); 172e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall try { 173e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall if (!mClosed && mWaiters.isEmpty() && mTickets >= 1) { 174e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mTickets--; 175e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall updateAvailableTicketCount(); 17612f608f3d2089439a108788a1908941eea4277b9Puneet Lall return new TicketImpl(); 177e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } else { 178e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall return null; 17912f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 180e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } finally { 181e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mLock.unlock(); 18212f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 18312f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 18412f608f3d2089439a108788a1908941eea4277b9Puneet Lall 18512f608f3d2089439a108788a1908941eea4277b9Puneet Lall @Override 18612f608f3d2089439a108788a1908941eea4277b9Puneet Lall public void close() { 187e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mLock.lock(); 188e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall try { 189e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall if (mClosed) { 190e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall return; 191e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 19212f608f3d2089439a108788a1908941eea4277b9Puneet Lall 193e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mClosed = true; 194e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 195e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall for (Waiter waiter : mWaiters) { 196e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall waiter.getCondition().signal(); 197e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 198e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall 199e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall updateAvailableTicketCount(); 200e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } finally { 201e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall mLock.unlock(); 202e919a48fb40b9d6c698a495acf40adbc0e320431Puneet Lall } 20312f608f3d2089439a108788a1908941eea4277b9Puneet Lall } 20412f608f3d2089439a108788a1908941eea4277b9Puneet Lall} 205