ConcurrentState.java revision 9c94ab32a69a1ad3642a0f1e38e68bcfd97d3511
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
17package com.android.camera.async;
18
19import java.util.ArrayList;
20import java.util.HashSet;
21import java.util.List;
22import java.util.Set;
23import java.util.concurrent.Executor;
24
25import com.android.camera.util.Callback;
26
27/**
28 * Generic asynchronous state wrapper which supports two methods of interaction:
29 * polling for the latest value and listening for updates.
30 * <p>
31 * Note that this class only supports polling and using listeners. If
32 * synchronous consumption of state changes is required, see
33 * {@link FutureResult} or {@link BufferQueue} and its implementations.
34 * </p>
35 */
36public class ConcurrentState<T> implements Updatable<T>, Pollable<T> {
37
38    private static class ExecutorListenerPair<T> {
39        private final Executor mExecutor;
40        private final Callback<T> mListener;
41
42        public ExecutorListenerPair(Executor executor, Callback<T> listener) {
43            mExecutor = executor;
44            mListener = listener;
45        }
46
47        /**
48         * Runs the callback on the executor with the given value.
49         */
50        public void run(final T t) {
51            mExecutor.execute(new Runnable() {
52                public void run() {
53                    mListener.onCallback(t);
54                }
55            });
56        }
57    }
58
59    private final Object mLock;
60    private final Set<ExecutorListenerPair<T>> mListeners;
61    private boolean mValueSet;
62    private T mValue;
63
64    public ConcurrentState() {
65        mLock = new Object();
66        mListeners = new HashSet<ExecutorListenerPair<T>>();
67        mValueSet = false;
68    }
69
70    /**
71     * Updates the state to the latest value, notifying all listeners.
72     */
73    @Override
74    public void update(T newValue) {
75        List<ExecutorListenerPair<T>> listeners = new ArrayList<ExecutorListenerPair<T>>();
76        synchronized (mLock) {
77            mValueSet = true;
78            mValue = newValue;
79            // Copy listeners out here so we can iterate over the list outside
80            // the critical section.
81            listeners.addAll(mListeners);
82        }
83        for (ExecutorListenerPair<T> pair : listeners) {
84            pair.run(newValue);
85        }
86    }
87
88    /**
89     * Adds the given callback, returning a token to be closed when the callback
90     * is no longer needed.
91     *
92     * @param callback The callback to add.
93     * @param executor The executor on which the callback will be invoked.
94     * @return A {@link SafeCloseable} token to be closed when the callback must
95     *         be removed.
96     */
97    public SafeCloseable addCallback(Callback callback, Executor executor) {
98        synchronized (mLock) {
99            final ExecutorListenerPair<T> pair = new ExecutorListenerPair<>(executor, callback);
100            mListeners.add(pair);
101
102            return new SafeCloseable() {
103                @Override
104                public void close() {
105                    synchronized (mLock) {
106                        mListeners.remove(pair);
107                    }
108                }
109            };
110        }
111    }
112
113    /**
114     * Polls for the latest value.
115     *
116     * @return The latest state, or defaultValue if no state has been set yet.
117     */
118    @Override
119    public T get(T defaultValue) {
120        try {
121            return get();
122        } catch (Pollable.NoValueSetException e) {
123            return defaultValue;
124        }
125    }
126
127    /**
128     * Polls for the latest value.
129     *
130     * @return The latest state.
131     * @throws com.android.camera.async.Pollable.NoValueSetException If no value has been set yet.
132     */
133    @Override
134    public T get() throws Pollable.NoValueSetException {
135        synchronized (mLock) {
136            if (mValueSet) {
137                return mValue;
138            } else {
139                throw new Pollable.NoValueSetException();
140            }
141        }
142    }
143}
144