Merge "Fix ConcurrentModificationException in AcceptOnceConsumer" into tm-qpr-dev am: 8642c7f747 am: c7fe2bfad8

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20613664

Change-Id: I11da72bf15b56d90a397800e7a9e6a2cd8e25860
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Kenneth Ford
2022-12-16 00:01:31 +00:00
committed by Automerger Merge Worker
2 changed files with 43 additions and 4 deletions

View File

@@ -27,9 +27,10 @@ import java.util.function.Consumer;
*/
public class AcceptOnceConsumer<T> implements Consumer<T> {
private final Consumer<T> mCallback;
private final DataProducer<T> mProducer;
private final AcceptOnceProducerCallback<T> mProducer;
public AcceptOnceConsumer(@NonNull DataProducer<T> producer, @NonNull Consumer<T> callback) {
public AcceptOnceConsumer(@NonNull AcceptOnceProducerCallback<T> producer,
@NonNull Consumer<T> callback) {
mProducer = producer;
mCallback = callback;
}
@@ -37,6 +38,20 @@ public class AcceptOnceConsumer<T> implements Consumer<T> {
@Override
public void accept(@NonNull T t) {
mCallback.accept(t);
mProducer.removeDataChangedCallback(this);
mProducer.onConsumerReadyToBeRemoved(this);
}
/**
* Interface to allow the {@link AcceptOnceConsumer} to notify the client that created it,
* when it is ready to be removed. This allows the client to remove the consumer object
* when it deems it is safe to do so.
* @param <T> The type of data this callback accepts through {@link #onConsumerReadyToBeRemoved}
*/
public interface AcceptOnceProducerCallback<T> {
/**
* Notifies that the given {@code callback} is ready to be removed
*/
void onConsumerReadyToBeRemoved(Consumer<T> callback);
}
}

View File

@@ -19,6 +19,7 @@ package androidx.window.util;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
@@ -31,11 +32,14 @@ import java.util.function.Consumer;
*
* @param <T> The type of data this producer returns through {@link DataProducer#getData}.
*/
public abstract class BaseDataProducer<T> implements DataProducer<T> {
public abstract class BaseDataProducer<T> implements DataProducer<T>,
AcceptOnceConsumer.AcceptOnceProducerCallback<T> {
private final Object mLock = new Object();
@GuardedBy("mLock")
private final Set<Consumer<T>> mCallbacks = new LinkedHashSet<>();
@GuardedBy("mLock")
private final Set<Consumer<T>> mCallbacksToRemove = new HashSet<>();
/**
* Adds a callback to the set of callbacks listening for data. Data is delivered through
@@ -85,6 +89,26 @@ public abstract class BaseDataProducer<T> implements DataProducer<T> {
for (Consumer<T> callback : mCallbacks) {
callback.accept(value);
}
removeFinishedCallbacksLocked();
}
}
/**
* Removes any callbacks that notified us through {@link #onConsumerReadyToBeRemoved(Consumer)}
* that they are ready to be removed.
*/
@GuardedBy("mLock")
private void removeFinishedCallbacksLocked() {
for (Consumer<T> callback: mCallbacksToRemove) {
mCallbacks.remove(callback);
}
mCallbacksToRemove.clear();
}
@Override
public void onConsumerReadyToBeRemoved(Consumer<T> callback) {
synchronized (mLock) {
mCallbacksToRemove.add(callback);
}
}
}