• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Nullable;
8 
9 import org.chromium.base.Callback;
10 import org.chromium.base.Promise;
11 import org.chromium.base.ThreadUtils;
12 
13 /**
14  * Abstract implementation of {@link LazySupplier} to be used by classes providing it as a
15  * dependency to others. A call to {@link LazyOneshotSupplier#get()} will attempt to set the
16  * supplied object via {@link LazyOneshotSupplier#doSet()}. Additionally, {@link
17  * LazyOneshotSupplier#onAvailable(Callback<T>)} will not call {@link LazyOneshotSupplier#get()}
18  * unless it already has a value to prevent eager initialization. The supplied value can be null,
19  * {@link LazyOneshotSupplier#hasValue} should be used to differentiate between un/set states.
20  *
21  * <p>If eager initialization in response to {@link LazyOneshotSupplier#onAvailable(Callback<T>)} is
22  * required then a call to {@link LazyOneshotSupplier#get()} can be made just before attaching the
23  * callback.
24  *
25  * <p>Instances of this class must only be accessed from the thread they were created on.
26  *
27  * <p>To use:
28  *
29  * <ol>
30  *   <li>Create a new {@code LazyOneshotSupplier<T>} to pass as a dependency.
31  *   <li>Override {@link #doSet()} to invoke {@link #set(T)}. This will be invoked when {@link
32  *       #get()} is invoked if {@link #hasValue()} returns false. Note that invoking {@link
33  *       #doSet()} does not have to invoke {@link #set(T)} if there is reason not to such as
34  *       awaiting an async dependency. However, if this is the case clients of the supplier need to
35  *       be careful to properly understand the initialization lifecycle.
36  * </ol>
37  *
38  * @param <T> The type of the wrapped object.
39  */
40 public abstract class LazyOneshotSupplierImpl<T> implements LazyOneshotSupplier<T> {
41     private final Promise<T> mPromise = new Promise<>();
42     private final ThreadUtils.ThreadChecker mThreadChecker = new ThreadUtils.ThreadChecker();
43 
44     private boolean mDoSetCalled;
45 
46     /**
47      * Lazily invokes the callback the first time {@link #set(T)} is invoked or immediately if
48      * already available.
49      */
50     @Override
onAvailable(Callback<T> callback)51     public void onAvailable(Callback<T> callback) {
52         mThreadChecker.assertOnValidThread();
53         mPromise.then(callback);
54     }
55 
56     /**
57      * Return the value of the supplier. Calling this the first time will initialize the value in
58      * the supplier via {@link #doSet()}.
59      *
60      * @return the value that was provided in {@link #set(T)} or null.
61      */
62     @Override
get()63     public @Nullable T get() {
64         mThreadChecker.assertOnValidThread();
65         if (!hasValue()) {
66             tryDoSet();
67         }
68         return hasValue() ? mPromise.getResult() : null;
69     }
70 
71     /** Returns whether a value is set in the supplier. */
72     @Override
hasValue()73     public boolean hasValue() {
74         return mPromise.isFulfilled();
75     }
76 
77     /**
78      * Sets the value upon first {@link #get()}. Implementers should override this to invoke {@link
79      * #set(T)}.
80      */
doSet()81     public abstract void doSet();
82 
83     /**
84      * Set the object supplied by this supplier. This will notify registered callbacks that the
85      * dependency is available. If set() has already been called, this method will assert.
86      *
87      * @param object The object to supply.
88      */
set(@ullable T object)89     public void set(@Nullable T object) {
90         mThreadChecker.assertOnValidThread();
91         assert !mPromise.isFulfilled();
92         mPromise.fulfill(object);
93     }
94 
tryDoSet()95     private void tryDoSet() {
96         if (mDoSetCalled) return;
97         doSet();
98         mDoSetCalled = true;
99     }
100 }
101