/* * Copyright (C) 2015 The Android Open Source Project * * 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 * * http://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 com.android.camera.one.v2.sharedimagereader.ringbuffer; import com.android.camera.async.ConcurrentState; import com.android.camera.async.Lifetime; import com.android.camera.async.Observable; import com.android.camera.async.SafeCloseable; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.concurrent.ThreadSafe; /** * Provides a listenable count of the total number of image capacity which are * readily-available at any given time. *

* The total count is the sum of all inputs. */ @ThreadSafe @ParametersAreNonnullByDefault final class AvailableTicketCounter implements Observable { private final List> mInputs; private final ConcurrentState mCount; private final AtomicInteger mCounterLocked; public AvailableTicketCounter(List> inputs) { mInputs = new ArrayList<>(inputs); mCount = new ConcurrentState<>(0); mCounterLocked = new AtomicInteger(0); } @Nonnull @Override public SafeCloseable addCallback(final Runnable callback, final Executor executor) { Lifetime callbackLifetime = new Lifetime(); for (Observable input : mInputs) { callbackLifetime.add(input.addCallback(callback, executor)); } return callbackLifetime; } private int compute() { int sum = 0; for (Observable input : mInputs) { sum += input.get(); } return sum; } @Nonnull @Override public Integer get() { int value = mCount.get(); if (mCounterLocked.get() == 0) { value = compute(); } return value; } /** * Locks the counter to the current value. Changes to the value, resulting * from changes to the inputs, will not be reflected by {@link #get} or be * propagated to callbacks until a matching call to {@link #unfreeze}. */ public void freeze() { int value = compute(); // Update the count with the current, valid value before freezing it, if // it was not already frozen. mCounterLocked.incrementAndGet(); mCount.update(value); } /** * @see {@link #freeze} */ public void unfreeze() { // If this invocation released the last logical "lock" on the counter, // then update with the latest value. // Note that the value used *must* be that recomputed before // decrementing the lock counter to guarantee that we only update // listeners with a valid value. int newValue = compute(); int numLocksHeld = mCounterLocked.decrementAndGet(); if (numLocksHeld == 0) { mCount.update(newValue); } } }