blob: b4fe503fd01c82a23a4de272a21561385d528f8d [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base.supplier;
import androidx.annotation.NonNull;
import org.chromium.base.Callback;
import org.chromium.base.ThreadUtils;
/** Utilities for interactions with Suppliers. */
public class SupplierUtils {
private SupplierUtils() {}
private static class Barrier {
private final ThreadUtils.ThreadChecker mThreadChecker = new ThreadUtils.ThreadChecker();
private int mWaitingCount;
private Runnable mCallback;
void waitForAll(Runnable callback, Supplier... suppliers) {
mThreadChecker.assertOnValidThread();
assert mCallback == null;
mCallback = callback;
int waitingSupplierCount = 0;
Callback<?> supplierCallback = (unused) -> onSupplierAvailable();
for (Supplier<?> supplier : suppliers) {
if (supplier.hasValue()) continue;
waitingSupplierCount++;
if (supplier instanceof ObservableSupplier) {
ObservableSupplier<?> observableSupplier = ((ObservableSupplier) supplier);
new OneShotCallback(observableSupplier, supplierCallback);
} else if (supplier instanceof OneshotSupplier) {
((OneshotSupplier) supplier).onAvailable(supplierCallback);
} else if (supplier instanceof SyncOneshotSupplier) {
((SyncOneshotSupplier) supplier).onAvailable(supplierCallback);
} else {
assert false
: "Unexpected Supplier type that does not already have a value: "
+ supplier;
}
}
mWaitingCount = waitingSupplierCount;
notifyCallbackIfAppropriate();
}
private void onSupplierAvailable() {
mThreadChecker.assertOnValidThread();
mWaitingCount--;
assert mWaitingCount >= 0;
notifyCallbackIfAppropriate();
}
private void notifyCallbackIfAppropriate() {
if (mWaitingCount != 0) return;
if (mCallback == null) return;
mCallback.run();
mCallback = null;
}
}
/**
* Waits for all suppliers to have assigned values, and when that happens, notifies the
* specified callback.
*
* <p>If all suppliers already have values, then the callback will be notified synchronously.
*
* <p>To prevent leaking objects, it is recommended to use {@link
* org.chromium.base.CallbackController} for the {@link Runnable} callback.
*
* <p>Not thread safe. All passed in suppliers must be notified on the same thread this method
* is called.
*
* @param callback The callback to be notified when all suppliers have values set.
* @param suppliers The list of suppliers to check for values.
*/
public static void waitForAll(@NonNull Runnable callback, Supplier... suppliers) {
assert callback != null;
new Barrier().waitForAll(callback, suppliers);
}
}