1 /* 2 * Copyright (C) 2014 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.inputmethod.latin.utils; 18 19 import android.util.Log; 20 21 import com.android.inputmethod.annotations.UsedForTesting; 22 23 import java.lang.Thread.UncaughtExceptionHandler; 24 import java.util.concurrent.Executors; 25 import java.util.concurrent.ScheduledExecutorService; 26 import java.util.concurrent.ThreadFactory; 27 import java.util.concurrent.TimeUnit; 28 29 /** 30 * Utilities to manage executors. 31 */ 32 public class ExecutorUtils { 33 34 private static final String TAG = "ExecutorUtils"; 35 36 public static final String KEYBOARD = "Keyboard"; 37 public static final String SPELLING = "Spelling"; 38 39 private static ScheduledExecutorService sKeyboardExecutorService = newExecutorService(KEYBOARD); 40 private static ScheduledExecutorService sSpellingExecutorService = newExecutorService(SPELLING); 41 newExecutorService(final String name)42 private static ScheduledExecutorService newExecutorService(final String name) { 43 return Executors.newSingleThreadScheduledExecutor(new ExecutorFactory(name)); 44 } 45 46 private static class ExecutorFactory implements ThreadFactory { 47 private final String mName; 48 ExecutorFactory(final String name)49 private ExecutorFactory(final String name) { 50 mName = name; 51 } 52 53 @Override newThread(final Runnable runnable)54 public Thread newThread(final Runnable runnable) { 55 Thread thread = new Thread(runnable, TAG); 56 thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { 57 @Override 58 public void uncaughtException(Thread thread, Throwable ex) { 59 Log.w(mName + "-" + runnable.getClass().getSimpleName(), ex); 60 } 61 }); 62 return thread; 63 } 64 } 65 66 @UsedForTesting 67 private static ScheduledExecutorService sExecutorServiceForTests; 68 69 @UsedForTesting setExecutorServiceForTests( final ScheduledExecutorService executorServiceForTests)70 public static void setExecutorServiceForTests( 71 final ScheduledExecutorService executorServiceForTests) { 72 sExecutorServiceForTests = executorServiceForTests; 73 } 74 75 // 76 // Public methods used to schedule a runnable for execution. 77 // 78 79 /** 80 * @param name Executor's name. 81 * @return scheduled executor service used to run background tasks 82 */ getBackgroundExecutor(final String name)83 public static ScheduledExecutorService getBackgroundExecutor(final String name) { 84 if (sExecutorServiceForTests != null) { 85 return sExecutorServiceForTests; 86 } 87 switch (name) { 88 case KEYBOARD: 89 return sKeyboardExecutorService; 90 case SPELLING: 91 return sSpellingExecutorService; 92 default: 93 throw new IllegalArgumentException("Invalid executor: " + name); 94 } 95 } 96 killTasks(final String name)97 public static void killTasks(final String name) { 98 final ScheduledExecutorService executorService = getBackgroundExecutor(name); 99 executorService.shutdownNow(); 100 try { 101 executorService.awaitTermination(5, TimeUnit.SECONDS); 102 } catch (InterruptedException e) { 103 Log.wtf(TAG, "Failed to shut down: " + name); 104 } 105 if (executorService == sExecutorServiceForTests) { 106 // Don't do anything to the test service. 107 return; 108 } 109 switch (name) { 110 case KEYBOARD: 111 sKeyboardExecutorService = newExecutorService(KEYBOARD); 112 break; 113 case SPELLING: 114 sSpellingExecutorService = newExecutorService(SPELLING); 115 break; 116 default: 117 throw new IllegalArgumentException("Invalid executor: " + name); 118 } 119 } 120 121 @UsedForTesting chain(final Runnable... runnables)122 public static Runnable chain(final Runnable... runnables) { 123 return new RunnableChain(runnables); 124 } 125 126 @UsedForTesting 127 public static class RunnableChain implements Runnable { 128 private final Runnable[] mRunnables; 129 RunnableChain(final Runnable... runnables)130 private RunnableChain(final Runnable... runnables) { 131 if (runnables == null || runnables.length == 0) { 132 throw new IllegalArgumentException("Attempting to construct an empty chain"); 133 } 134 mRunnables = runnables; 135 } 136 137 @UsedForTesting getRunnables()138 public Runnable[] getRunnables() { 139 return mRunnables; 140 } 141 142 @Override run()143 public void run() { 144 for (Runnable runnable : mRunnables) { 145 if (Thread.interrupted()) { 146 return; 147 } 148 runnable.run(); 149 } 150 } 151 } 152 } 153