1 /*
2  * Copyright 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 package androidx.graphics.lowlatency
17 
18 import java.util.concurrent.locks.ReentrantLock
19 import kotlin.concurrent.withLock
20 
21 /**
22  * Thread-safe class responsible for maintaining a list of objects as well as an index that keeps
23  * track of the last consumed object within the queue. This ensures that the collection as well as
24  * the current index parameter are updated atomically within the same lock.
25  */
26 internal class ParamQueue<T> {
27 
28     private val mLock = ReentrantLock()
29     private var mParams = ArrayList<T>()
30     private var mIndex = 0
31 
32     /** Clears the parameter queue and resets the index position to 0 */
clearnull33     fun clear() {
34         mLock.withLock {
35             mIndex = 0
36             mParams.clear()
37         }
38     }
39 
40     /**
41      * Returns the current queue and resets the index position to 0. This collection is no longer
42      * owned by [ParamQueue] and a new queue is maintained after this call is made. This allows
43      * callers to manipulate the returned collection as they see fit without impacting the integrity
44      * of the [ParamQueue] after this method is invoked.
45      */
releasenull46     fun release(): MutableCollection<T> {
47         mLock.withLock {
48             val result = mParams
49             mParams = ArrayList<T>()
50             mIndex = 0
51             return result
52         }
53     }
54 
55     /**
56      * Returns the next parameter at the index position and increments the index position. This
57      * parameter is provided as a callback. If the index position is out of range, this method acts
58      * as a no-op. This does not actually remove the parameter from the collection. Consumers must
59      * either call [clear], or clear the collection returned in [release] to ensure contents do not
60      * grow unbounded.
61      */
nextnull62     inline fun next(block: (T) -> Unit) {
63         mLock.withLock {
64             if (mIndex < mParams.size) {
65                 val param = mParams[mIndex++]
66                 block(param)
67             }
68         }
69     }
70 
71     /**
72      * Similar to [next], but iterates through each item from the current index to the end of the
73      * collection and invokes the given lambda on each parameter. This does not actually remove the
74      * parameter from the collection. Consumers must either call [clear], or clear the collection
75      * returned in [release] to ensure contents do not grow unbounded.
76      */
flushnull77     inline fun flush(block: (T) -> Unit) {
78         mLock.withLock {
79             while (mIndex < mParams.size) {
80                 val param = mParams[mIndex++]
81                 block(param)
82             }
83         }
84     }
85 
86     /**
87      * Adds a new entry into the parameter queue. This leaves the current index position unchanged.
88      */
addnull89     fun add(param: T) {
90         mLock.withLock { mParams.add(param) }
91     }
92 
addAllnull93     fun addAll(params: Collection<T>) {
94         mLock.withLock { mParams.addAll(params) }
95     }
96 
<lambda>null97     fun count(): Int = mLock.withLock { mParams.size }
98 
isEmptynull99     fun isEmpty() = count() == 0
100 }
101