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