1 // Copyright 2023 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base.supplier; 6 7 import androidx.annotation.NonNull; 8 9 import org.chromium.base.Callback; 10 import org.chromium.base.ThreadUtils; 11 12 /** Utilities for interactions with Suppliers. */ 13 public class SupplierUtils { SupplierUtils()14 private SupplierUtils() {} 15 16 private static class Barrier { 17 private final ThreadUtils.ThreadChecker mThreadChecker = new ThreadUtils.ThreadChecker(); 18 private int mWaitingCount; 19 private Runnable mCallback; 20 waitForAll(Runnable callback, Supplier... suppliers)21 void waitForAll(Runnable callback, Supplier... suppliers) { 22 mThreadChecker.assertOnValidThread(); 23 assert mCallback == null; 24 mCallback = callback; 25 int waitingSupplierCount = 0; 26 Callback<?> supplierCallback = (unused) -> onSupplierAvailable(); 27 for (Supplier<?> supplier : suppliers) { 28 if (supplier.hasValue()) continue; 29 30 waitingSupplierCount++; 31 if (supplier instanceof ObservableSupplier) { 32 ObservableSupplier<?> observableSupplier = ((ObservableSupplier) supplier); 33 new OneShotCallback(observableSupplier, supplierCallback); 34 } else if (supplier instanceof OneshotSupplier) { 35 ((OneshotSupplier) supplier).onAvailable(supplierCallback); 36 } else if (supplier instanceof SyncOneshotSupplier) { 37 ((SyncOneshotSupplier) supplier).onAvailable(supplierCallback); 38 } else { 39 assert false 40 : "Unexpected Supplier type that does not already have a value: " 41 + supplier; 42 } 43 } 44 mWaitingCount = waitingSupplierCount; 45 notifyCallbackIfAppropriate(); 46 } 47 onSupplierAvailable()48 private void onSupplierAvailable() { 49 mThreadChecker.assertOnValidThread(); 50 mWaitingCount--; 51 assert mWaitingCount >= 0; 52 notifyCallbackIfAppropriate(); 53 } 54 notifyCallbackIfAppropriate()55 private void notifyCallbackIfAppropriate() { 56 if (mWaitingCount != 0) return; 57 if (mCallback == null) return; 58 mCallback.run(); 59 mCallback = null; 60 } 61 } 62 63 /** 64 * Waits for all suppliers to have assigned values, and when that happens, notifies the 65 * specified callback. 66 * 67 * <p>If all suppliers already have values, then the callback will be notified synchronously. 68 * 69 * <p>To prevent leaking objects, it is recommended to use {@link 70 * org.chromium.base.CallbackController} for the {@link Runnable} callback. 71 * 72 * <p>Not thread safe. All passed in suppliers must be notified on the same thread this method 73 * is called. 74 * 75 * @param callback The callback to be notified when all suppliers have values set. 76 * @param suppliers The list of suppliers to check for values. 77 */ waitForAll(@onNull Runnable callback, Supplier... suppliers)78 public static void waitForAll(@NonNull Runnable callback, Supplier... suppliers) { 79 assert callback != null; 80 new Barrier().waitForAll(callback, suppliers); 81 } 82 } 83