Home | History | Annotate | Download | only in ringbuffer
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.camera.one.v2.sharedimagereader.ringbuffer;
     18 
     19 import com.android.camera.async.ConcurrentState;
     20 import com.android.camera.async.Lifetime;
     21 import com.android.camera.async.Observable;
     22 import com.android.camera.async.SafeCloseable;
     23 
     24 import java.util.ArrayList;
     25 import java.util.List;
     26 import java.util.concurrent.Executor;
     27 import java.util.concurrent.atomic.AtomicInteger;
     28 
     29 import javax.annotation.Nonnull;
     30 import javax.annotation.ParametersAreNonnullByDefault;
     31 import javax.annotation.concurrent.ThreadSafe;
     32 
     33 /**
     34  * Provides a listenable count of the total number of image capacity which are
     35  * readily-available at any given time.
     36  * <p>
     37  * The total count is the sum of all inputs.
     38  */
     39 @ThreadSafe
     40 @ParametersAreNonnullByDefault
     41 final class AvailableTicketCounter implements Observable<Integer> {
     42     private final List<Observable<Integer>> mInputs;
     43     private final ConcurrentState<Integer> mCount;
     44     private final AtomicInteger mCounterLocked;
     45 
     46     public AvailableTicketCounter(List<Observable<Integer>> inputs) {
     47         mInputs = new ArrayList<>(inputs);
     48         mCount = new ConcurrentState<>(0);
     49         mCounterLocked = new AtomicInteger(0);
     50     }
     51 
     52     @Nonnull
     53     @Override
     54     public SafeCloseable addCallback(final Runnable callback, final Executor executor) {
     55         Lifetime callbackLifetime = new Lifetime();
     56         for (Observable<Integer> input : mInputs) {
     57             callbackLifetime.add(input.addCallback(callback, executor));
     58         }
     59         return callbackLifetime;
     60     }
     61 
     62     private int compute() {
     63         int sum = 0;
     64         for (Observable<Integer> input : mInputs) {
     65             sum += input.get();
     66         }
     67         return sum;
     68     }
     69 
     70     @Nonnull
     71     @Override
     72     public Integer get() {
     73         int value = mCount.get();
     74         if (mCounterLocked.get() == 0) {
     75             value = compute();
     76         }
     77         return value;
     78     }
     79 
     80     /**
     81      * Locks the counter to the current value. Changes to the value, resulting
     82      * from changes to the inputs, will not be reflected by {@link #get} or be
     83      * propagated to callbacks until a matching call to {@link #unfreeze}.
     84      */
     85     public void freeze() {
     86         int value = compute();
     87         // Update the count with the current, valid value before freezing it, if
     88         // it was not already frozen.
     89         mCounterLocked.incrementAndGet();
     90         mCount.update(value);
     91     }
     92 
     93     /**
     94      * @see {@link #freeze}
     95      */
     96     public void unfreeze() {
     97         // If this invocation released the last logical "lock" on the counter,
     98         // then update with the latest value.
     99         // Note that the value used *must* be that recomputed before
    100         // decrementing the lock counter to guarantee that we only update
    101         // listeners with a valid value.
    102         int newValue = compute();
    103         int numLocksHeld = mCounterLocked.decrementAndGet();
    104         if (numLocksHeld == 0) {
    105             mCount.update(newValue);
    106         }
    107     }
    108 }
    109