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 
21 /**
22  * Executes NamedTasks in batches of a given size. Tasks are queued until executeNextBatch is
23  * called.
24  * @param executor A SourceTaskExecutor for actually executing the tasks.
25  */
26 class BatchingNamedTaskExecutor(private val mExecutor: NamedTaskExecutor) : NamedTaskExecutor {
27   /** Queue of tasks waiting to be dispatched to mExecutor */
28   private val mQueuedTasks: ArrayList<NamedTask?> = arrayListOf()
executenull29   override fun execute(task: NamedTask?) {
30     synchronized(mQueuedTasks) {
31       if (DBG) Log.d(TAG, "Queuing $task")
32       mQueuedTasks.add(task)
33     }
34   }
35 
dispatchnull36   private fun dispatch(task: NamedTask?) {
37     if (DBG) Log.d(TAG, "Dispatching $task")
38     mExecutor.execute(task)
39   }
40 
41   /**
42    * Instructs the executor to submit the next batch of results.
43    * @param batchSize the maximum number of entries to execute.
44    */
executeNextBatchnull45   fun executeNextBatch(batchSize: Int) {
46     var batch = arrayOfNulls<NamedTask?>(0)
47     synchronized(mQueuedTasks) {
48       val count: Int = Math.min(mQueuedTasks.size, batchSize)
49       val nextTasks: ArrayList<NamedTask?> = mQueuedTasks.subList(0, count) as ArrayList<NamedTask?>
50       batch = nextTasks.toArray(batch)
51       nextTasks.clear()
52       if (DBG) Log.d(TAG, "Dispatching batch of $count")
53     }
54     for (task in batch) {
55       dispatch(task)
56     }
57   }
58 
59   /**
60    * Cancel any un-started tasks running in this executor. This instance should not be re-used after
61    * calling this method.
62    */
cancelPendingTasksnull63   override fun cancelPendingTasks() {
64     synchronized(mQueuedTasks) { mQueuedTasks.clear() }
65   }
66 
closenull67   override fun close() {
68     cancelPendingTasks()
69     mExecutor.close()
70   }
71 
72   companion object {
73     private const val DBG = false
74     private const val TAG = "QSB.BatchingNamedTaskExecutor"
75   }
76 }
77