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