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