/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.quicksearchbox.util import android.util.Log import kotlin.collections.MutableList /** * Abstract base class for a one-place cache that holds a value that is produced asynchronously. * * @param The type of the data held in the cache. */ abstract class CachedLater : NowOrLater { private val mLock: Any = Any() private var mValue: A? = null private var mCreating = false private var mValid = false private var mWaitingConsumers: MutableList>? = null /** * Creates the object to store in the cache. This method must call [.store] when it's done. This * method must not block. */ protected abstract fun create() /** Saves a new value to the cache. */ protected fun store(value: A) { if (DBG) Log.d(TAG, "store()") var waitingConsumers: MutableList>? synchronized(mLock) { mValue = value mValid = true mCreating = false waitingConsumers = mWaitingConsumers mWaitingConsumers = null } if (waitingConsumers != null) { for (consumer in waitingConsumers!!) { if (DBG) Log.d(TAG, "Calling consumer: $consumer") consumer.consume(value) } } } /** * Gets the value. * * @param consumer A consumer that will be given the cached value. The consumer may be called * synchronously, or asynchronously on an unspecified thread. */ override fun getLater(consumer: Consumer?) { if (DBG) Log.d(TAG, "getLater()") var valid: Boolean var value: A? synchronized(mLock) { valid = mValid value = mValue if (!valid) { if (mWaitingConsumers == null) { mWaitingConsumers = mutableListOf() } mWaitingConsumers?.add(consumer!!) } } if (valid) { if (DBG) Log.d(TAG, "valid, calling consumer synchronously") consumer!!.consume(value!!) } else { var create = false synchronized(mLock) { if (!mCreating) { mCreating = true create = true } } if (create) { if (DBG) Log.d(TAG, "not valid, calling create()") create() } else { if (DBG) Log.d(TAG, "not valid, already creating") } } } /** Clears the cache. */ fun clear() { if (DBG) Log.d(TAG, "clear()") synchronized(mLock) { mValue = null mValid = false } } override fun haveNow(): Boolean { synchronized(mLock) { return mValid } } @get:Synchronized override val now: A get() { synchronized(mLock) { if (!haveNow()) { throw IllegalStateException("getNow() called when haveNow() is false") } return mValue!! } } companion object { private const val TAG = "QSB.AsyncCache" private const val DBG = false } }