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 import java.util.concurrent.LinkedBlockingQueue
21 import java.util.concurrent.ThreadFactory
22 
23 /** Executor that uses a single thread and an unbounded work queue. */
24 class SingleThreadNamedTaskExecutor(threadFactory: ThreadFactory?) : NamedTaskExecutor {
25   private val mQueue: LinkedBlockingQueue<NamedTask>
26   private val mWorker: Thread
27 
28   @Volatile private var mClosed = false
cancelPendingTasksnull29   override fun cancelPendingTasks() {
30     if (DBG) Log.d(TAG, "Cancelling " + mQueue.size.toString() + " tasks: " + mWorker.name)
31     if (mClosed) {
32       throw IllegalStateException("cancelPendingTasks() after close()")
33     }
34     mQueue.clear()
35   }
36 
closenull37   override fun close() {
38     mClosed = true
39     mWorker.interrupt()
40     mQueue.clear()
41   }
42 
executenull43   override fun execute(task: NamedTask?) {
44     if (mClosed) {
45       throw IllegalStateException("execute() after close()")
46     }
47     mQueue.add(task)
48   }
49 
50   private inner class Worker : Runnable {
runnull51     override fun run() {
52       try {
53         loop()
54       } finally {
55         if (!mClosed) Log.w(TAG, "Worker exited before close")
56       }
57     }
58 
loopnull59     private fun loop() {
60       val currentThread: Thread = Thread.currentThread()
61       val threadName: String = currentThread.getName()
62       while (!mClosed) {
63         val task: NamedTask =
64           try {
65             mQueue.take()
66           } catch (ex: InterruptedException) {
67             continue
68           }
69         currentThread.setName(threadName + " " + task.name)
70         try {
71           if (DBG) Log.d(TAG, "Running task " + task.name)
72           task.run()
73           if (DBG) Log.d(TAG, "Task " + task.name + " complete")
74         } catch (ex: RuntimeException) {
75           Log.e(TAG, "Task " + task.name + " failed", ex)
76         }
77       }
78     }
79   }
80 
81   companion object {
82     private const val DBG = false
83     private const val TAG = "QSB.SingleThreadNamedTaskExecutor"
84     @JvmStatic
factorynull85     fun factory(threadFactory: ThreadFactory?): Factory<NamedTaskExecutor> {
86       return object : Factory<NamedTaskExecutor> {
87         override fun create(): NamedTaskExecutor {
88           return SingleThreadNamedTaskExecutor(threadFactory)
89         }
90       }
91     }
92   }
93 
94   init {
95     mQueue = LinkedBlockingQueue<NamedTask>()
96     mWorker = threadFactory!!.newThread(Worker())
97     mWorker.start()
98   }
99 }
100