ConcurrentState.java revision e919a48fb40b9d6c698a495acf40adbc0e320431
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 com.android.camera.util.Callback;
20
21import java.util.HashSet;
22import java.util.Set;
23import java.util.concurrent.Executor;
24
25import javax.annotation.CheckReturnValue;
26import javax.annotation.Nonnull;
27import javax.annotation.ParametersAreNonnullByDefault;
28
29/**
30 * Generic asynchronous state wrapper which supports two methods of interaction:
31 * polling for the latest value and listening for updates.
32 */
33@ParametersAreNonnullByDefault
34public class ConcurrentState<T> implements Updatable<T>, Observable<T> {
35
36    private static class ExecutorListenerPair<T> {
37        private final Executor mExecutor;
38        private final Callback<T> mListener;
39
40        public ExecutorListenerPair(Executor executor, Callback<T> listener) {
41            mExecutor = executor;
42            mListener = listener;
43        }
44
45        /**
46         * Runs the callback on the executor with the given value.
47         */
48        public void run(final T t) {
49            mExecutor.execute(new Runnable() {
50                @Override
51                public void run() {
52                    mListener.onCallback(t);
53                }
54            });
55        }
56    }
57
58    private final Object mLock;
59    private final Set<ExecutorListenerPair<? super T>> mListeners;
60    private T mValue;
61
62    public ConcurrentState(T initialValue) {
63        mLock = new Object();
64        mListeners = new HashSet<>();
65        mValue = initialValue;
66    }
67
68    /**
69     * Updates the state to the latest value, notifying all listeners.
70     */
71    @Override
72    public void update(T newValue) {
73        synchronized (mLock) {
74            mValue = newValue;
75            // Invoke executors.execute within mLock to guarantee that
76            // callbacks are serialized into their respective executor in
77            // the proper order.
78            for (ExecutorListenerPair<? super T> pair : mListeners) {
79                pair.run(newValue);
80            }
81        }
82    }
83
84    @CheckReturnValue
85    @Nonnull
86    @Override
87    public SafeCloseable addCallback(Callback<T> callback, Executor executor) {
88        synchronized (mLock) {
89            final ExecutorListenerPair<? super T> pair =
90                    new ExecutorListenerPair<>(executor, callback);
91            mListeners.add(pair);
92
93            return new SafeCloseable() {
94                @Override
95                public void close() {
96                    synchronized (mLock) {
97                        mListeners.remove(pair);
98                    }
99                }
100            };
101        }
102    }
103
104    /**
105     * Polls for the latest value.
106     *
107     * @return The latest state.
108     */
109    @Nonnull
110    @Override
111    public T get() {
112        synchronized (mLock) {
113            return mValue;
114        }
115    }
116}
117