Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c86fde17 authored by Kenneth Ford's avatar Kenneth Ford Committed by Automerger Merge Worker
Browse files

Merge "Fix ConcurrentModificationException in AcceptOnceConsumer" into...

Merge "Fix ConcurrentModificationException in AcceptOnceConsumer" into tm-qpr-dev am: 8642c7f7 am: c7fe2bfa

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



Change-Id: I11da72bf15b56d90a397800e7a9e6a2cd8e25860
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents b89b9679 c7fe2bfa
Loading
Loading
Loading
Loading
+18 −3
Original line number Original line Diff line number Diff line
@@ -27,9 +27,10 @@ import java.util.function.Consumer;
 */
 */
public class AcceptOnceConsumer<T> implements Consumer<T> {
public class AcceptOnceConsumer<T> implements Consumer<T> {
    private final Consumer<T> mCallback;
    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;
        mProducer = producer;
        mCallback = callback;
        mCallback = callback;
    }
    }
@@ -37,6 +38,20 @@ public class AcceptOnceConsumer<T> implements Consumer<T> {
    @Override
    @Override
    public void accept(@NonNull T t) {
    public void accept(@NonNull T t) {
        mCallback.accept(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);
    }
    }
}
}
+25 −1
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package androidx.window.util;
import androidx.annotation.GuardedBy;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.NonNull;


import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Optional;
import java.util.Set;
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}.
 * @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();
    private final Object mLock = new Object();
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private final Set<Consumer<T>> mCallbacks = new LinkedHashSet<>();
    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
     * 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) {
            for (Consumer<T> callback : mCallbacks) {
                callback.accept(value);
                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);
        }
        }
    }
    }
}
}
 No newline at end of file