ParamQueue.kt

/*
 * Copyright 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 androidx.graphics.lowlatency

import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock

/**
 * Thread-safe class responsible for maintaining a list of objects as well as an index
 * that keeps track of the last consumed object within the queue.
 * This ensures that the collection as well as the current index parameter are updated atomically
 * within the same lock.
 */
internal class ParamQueue<T> {

    private val mLock = ReentrantLock()
    private var mParams = ArrayList<T>()
    private var mIndex = 0

    /**
     * Clears the parameter queue and resets the index position to 0
     */
    fun clear() {
        mLock.withLock {
            mIndex = 0
            mParams.clear()
        }
    }

    /**
     * Returns the current queue and resets the index position to 0.
     * This collection is no longer owned by [ParamQueue] and a new queue is maintained after
     * this call is made. This allows callers to manipulate the returned collection as they
     * see fit without impacting the integrity of the [ParamQueue] after this method is invoked.
     */
    fun release(): MutableCollection<T> {
        mLock.withLock {
            val result = mParams
            mParams = ArrayList<T>()
            mIndex = 0
            return result
        }
    }

    /**
     * Returns the next parameter at the index position and increments the index position.
     * This parameter is provided as a callback. If the index position is out of range, this
     * method acts as a no-op.
     * This does not actually remove the parameter from the collection. Consumers must either
     * call [clear], or clear the collection returned in [release] to ensure contents do not
     * grow unbounded.
     */
    inline fun next(block: (T) -> Unit) {
        mLock.withLock {
            if (mIndex < mParams.size) {
                val param = mParams[mIndex++]
                block(param)
            }
        }
    }

    /**
     * Adds a new entry into the parameter queue. This leaves the current index position unchanged.
     */
    fun add(param: T) {
        mLock.withLock {
            mParams.add(param)
        }
    }

    fun count(): Int = mLock.withLock { mParams.size }

    fun isEmpty() = count() == 0
}