| /* |
| * 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 <A> The type of the data held in the cache. |
| */ |
| abstract class CachedLater<A> : NowOrLater<A> { |
| private val mLock: Any = Any() |
| private var mValue: A? = null |
| private var mCreating = false |
| private var mValid = false |
| private var mWaitingConsumers: MutableList<Consumer<in A>>? = 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<Consumer<in A>>? |
| 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<in A>?) { |
| 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 |
| } |
| } |