1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.quicksearchbox.util
18 
19 import android.util.Log
20 import kotlin.collections.MutableList
21 
22 /**
23  * Abstract base class for a one-place cache that holds a value that is produced asynchronously.
24  *
25  * @param <A> The type of the data held in the cache.
26  */
27 abstract class CachedLater<A> : NowOrLater<A> {
28   private val mLock: Any = Any()
29   private var mValue: A? = null
30   private var mCreating = false
31   private var mValid = false
32   private var mWaitingConsumers: MutableList<Consumer<in A>>? = null
33 
34   /**
35    * Creates the object to store in the cache. This method must call [.store] when it's done. This
36    * method must not block.
37    */
createnull38   protected abstract fun create()
39 
40   /** Saves a new value to the cache. */
41   protected fun store(value: A) {
42     if (DBG) Log.d(TAG, "store()")
43     var waitingConsumers: MutableList<Consumer<in A>>?
44     synchronized(mLock) {
45       mValue = value
46       mValid = true
47       mCreating = false
48       waitingConsumers = mWaitingConsumers
49       mWaitingConsumers = null
50     }
51     if (waitingConsumers != null) {
52       for (consumer in waitingConsumers!!) {
53         if (DBG) Log.d(TAG, "Calling consumer: $consumer")
54         consumer.consume(value)
55       }
56     }
57   }
58 
59   /**
60    * Gets the value.
61    *
62    * @param consumer A consumer that will be given the cached value. The consumer may be called
63    * synchronously, or asynchronously on an unspecified thread.
64    */
getLaternull65   override fun getLater(consumer: Consumer<in A>?) {
66     if (DBG) Log.d(TAG, "getLater()")
67     var valid: Boolean
68     var value: A?
69     synchronized(mLock) {
70       valid = mValid
71       value = mValue
72       if (!valid) {
73         if (mWaitingConsumers == null) {
74           mWaitingConsumers = mutableListOf()
75         }
76         mWaitingConsumers?.add(consumer!!)
77       }
78     }
79     if (valid) {
80       if (DBG) Log.d(TAG, "valid, calling consumer synchronously")
81       consumer!!.consume(value!!)
82     } else {
83       var create = false
84       synchronized(mLock) {
85         if (!mCreating) {
86           mCreating = true
87           create = true
88         }
89       }
90       if (create) {
91         if (DBG) Log.d(TAG, "not valid, calling create()")
92         create()
93       } else {
94         if (DBG) Log.d(TAG, "not valid, already creating")
95       }
96     }
97   }
98 
99   /** Clears the cache. */
clearnull100   fun clear() {
101     if (DBG) Log.d(TAG, "clear()")
102     synchronized(mLock) {
103       mValue = null
104       mValid = false
105     }
106   }
107 
haveNownull108   override fun haveNow(): Boolean {
109     synchronized(mLock) {
110       return mValid
111     }
112   }
113 
114   @get:Synchronized
115   override val now: A
116     get() {
<lambda>null117       synchronized(mLock) {
118         if (!haveNow()) {
119           throw IllegalStateException("getNow() called when haveNow() is false")
120         }
121         return mValue!!
122       }
123     }
124 
125   companion object {
126     private const val TAG = "QSB.AsyncCache"
127     private const val DBG = false
128   }
129 }
130