• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.async;
18 
19 import java.util.Set;
20 import java.util.concurrent.CopyOnWriteArraySet;
21 import java.util.concurrent.Executor;
22 
23 import javax.annotation.CheckReturnValue;
24 import javax.annotation.Nonnull;
25 import javax.annotation.ParametersAreNonnullByDefault;
26 
27 /**
28  * Generic asynchronous state wrapper which supports two methods of interaction:
29  * polling for the latest value and listening for updates.
30  */
31 @ParametersAreNonnullByDefault
32 public class ConcurrentState<T> implements Updatable<T>, Observable<T> {
33     private static class ExecutorListenerPair implements Runnable {
34         private final Executor mExecutor;
35         private final Runnable mListener;
36 
ExecutorListenerPair(Executor executor, Runnable listener)37         public ExecutorListenerPair(Executor executor, Runnable listener) {
38             mExecutor = executor;
39             mListener = listener;
40         }
41 
42         /**
43          * Runs the callback on the executor.
44          */
45         @Override
run()46         public void run() {
47             mExecutor.execute(mListener);
48         }
49     }
50 
51     private final Set<ExecutorListenerPair> mListeners;
52     private volatile T mValue;
53 
ConcurrentState(T initialValue)54     public ConcurrentState(T initialValue) {
55         // Callbacks are typically only added and removed at startup/shutdown,
56         // but {@link #update} is often called at high-frequency. So, using a
57         // read-optimized data structure is appropriate here.
58         mListeners = new CopyOnWriteArraySet<>();
59         mValue = initialValue;
60     }
61 
62     /**
63      * Updates the state to the latest value, notifying all listeners.
64      */
65     @Override
update(T newValue)66     public void update(T newValue) {
67         mValue = newValue;
68         for (ExecutorListenerPair pair : mListeners) {
69             pair.run();
70         }
71     }
72 
73     @CheckReturnValue
74     @Nonnull
75     @Override
addCallback(Runnable callback, Executor executor)76     public SafeCloseable addCallback(Runnable callback, Executor executor) {
77         final ExecutorListenerPair pair = new ExecutorListenerPair(executor, callback);
78         mListeners.add(pair);
79         return new SafeCloseable() {
80             @Override
81             public void close() {
82                 mListeners.remove(pair);
83             }
84         };
85     }
86 
87     /**
88      * Polls for the latest value.
89      *
90      * @return The latest state.
91      */
92     @Nonnull
93     @Override
94     public T get() {
95         return mValue;
96     }
97 }
98