chromium/base/android/java/src/org/chromium/base/supplier/SyncOneshotSupplierImpl.java

// 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 androidx.annotation.Nullable;

import org.chromium.base.Callback;
import org.chromium.base.ThreadUtils;

import java.util.ArrayList;

/**
 * A concrete implementation of {@link SyncOneshotSupplier} to used when callbacks need to be called
 * synchronously when a value is set. This differs from {@link OneshotSupplierImpl} in that the
 * pending {@link Callback}s are not posted when {@link #set(T)} is invoked.
 *
 * <p>Instances of this class must only be accessed from the thread they were created on.
 *
 * <p>To use:
 *
 * <ol>
 *   <li>Create a new {@code SyncOneshotSupplierImpl<T>} to pass as a dependency.
 *   <li>Call {@link #set(Object)} when the object becomes available. {@link #set(Object)} may only
 *       be called once.
 * </ol>
 *
 * @param <T> The type of the wrapped object.
 */
public class SyncOneshotSupplierImpl<T> implements SyncOneshotSupplier<T> {
    private final ThreadUtils.ThreadChecker mThreadChecker = new ThreadUtils.ThreadChecker();

    /** Lazily created list of pending callbacks to invoke when an object is set. */
    private @Nullable ArrayList<Callback<T>> mPendingCallbacks;

    private @Nullable T mObject;

    @Override
    public @Nullable T onAvailable(Callback<T> callback) {
        mThreadChecker.assertOnValidThread();
        T object = get();
        if (object != null) {
            callback.onResult(object);
        } else {
            if (mPendingCallbacks == null) {
                mPendingCallbacks = new ArrayList<Callback<T>>();
            }
            mPendingCallbacks.add(callback);
        }
        return object;
    }

    @Override
    public @Nullable T get() {
        mThreadChecker.assertOnValidThread();
        return mObject;
    }

    /**
     * Set the object supplied by this supplier. This will synchronously notify registered callbacks
     * that the dependency is available. If {@link #set(Object)} has already been called, this
     * method will assert.
     *
     * @param object The object to supply.
     */
    public void set(@NonNull T object) {
        mThreadChecker.assertOnValidThread();
        assert mObject == null;
        assert object != null;
        mObject = object;
        if (mPendingCallbacks == null) return;

        for (Callback<T> callback : mPendingCallbacks) {
            callback.onResult(object);
        }
        mPendingCallbacks = null;
    }
}