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