From eaa710d4aaac75ff2b7e29608d004fe7662b392e Mon Sep 17 00:00:00 2001 From: Dan Zivkovic Date: Thu, 12 Mar 2015 17:06:48 -0700 Subject: [PATCH] Separate executor for the Spelling decoder. Bug 19710676. Change-Id: I6e66eddd507c11e424105869833fe6841b90275d --- .../latin/ContactsContentObserver.java | 4 +- .../latin/DictionaryFacilitatorImpl.java | 2 +- .../latin/ExpandableBinaryDictionary.java | 2 +- .../spellcheck/UserDictionaryLookup.java | 6 +- .../latin/utils/ExecutorUtils.java | 57 +++++++++++++++---- .../latin/utils/ExecutorUtilsTests.java | 3 +- 6 files changed, 54 insertions(+), 20 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/ContactsContentObserver.java b/java/src/com/android/inputmethod/latin/ContactsContentObserver.java index 4a8d1133b..5eb9b16d1 100644 --- a/java/src/com/android/inputmethod/latin/ContactsContentObserver.java +++ b/java/src/com/android/inputmethod/latin/ContactsContentObserver.java @@ -56,8 +56,8 @@ public class ContactsContentObserver implements Runnable { mContentObserver = new ContentObserver(null /* handler */) { @Override public void onChange(boolean self) { - // TODO(zivkovic): Schedule a separate task to reset the decoder. - ExecutorUtils.getBackgroundExecutor().execute(ContactsContentObserver.this); + ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD) + .execute(ContactsContentObserver.this); } }; final ContentResolver contentResolver = mContext.getContentResolver(); diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java index 2f3c58251..a59253aad 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java @@ -338,7 +338,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { final Locale locale, final DictionaryInitializationListener listener) { final CountDownLatch latchForWaitingLoadingMainDictionary = new CountDownLatch(1); mLatchForWaitingLoadingMainDictionaries = latchForWaitingLoadingMainDictionary; - ExecutorUtils.getBackgroundExecutor().execute(new Runnable() { + ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute(new Runnable() { @Override public void run() { doReloadUninitializedMainDictionaries( diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 138a2ea5c..fa8fb2028 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -168,7 +168,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } private static void asyncExecuteTaskWithLock(final Lock lock, final Runnable task) { - ExecutorUtils.getBackgroundExecutor().execute(new Runnable() { + ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute(new Runnable() { @Override public void run() { lock.lock(); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/UserDictionaryLookup.java b/java/src/com/android/inputmethod/latin/spellcheck/UserDictionaryLookup.java index 6ab741ca1..f2491f478 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/UserDictionaryLookup.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/UserDictionaryLookup.java @@ -143,8 +143,8 @@ public class UserDictionaryLookup implements Closeable { } // Schedule a new reload after RELOAD_DELAY_MS. - mReloadFuture = ExecutorUtils.getBackgroundExecutor().schedule( - mLoader, RELOAD_DELAY_MS, TimeUnit.MILLISECONDS); + mReloadFuture = ExecutorUtils.getBackgroundExecutor(ExecutorUtils.SPELLING) + .schedule(mLoader, RELOAD_DELAY_MS, TimeUnit.MILLISECONDS); } } private final ContentObserver mObserver = new UserDictionaryContentObserver(); @@ -186,7 +186,7 @@ public class UserDictionaryLookup implements Closeable { // Schedule the initial load to run immediately. It's possible that the first call to // isValidWord occurs before the dictionary has actually loaded, so it should not // assume that the dictionary has been loaded. - ExecutorUtils.getBackgroundExecutor().execute(mLoader); + ExecutorUtils.getBackgroundExecutor(ExecutorUtils.SPELLING).execute(mLoader); // Register the observer to be notified on changes to the UserDictionary and all individual // items. diff --git a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java index a78446103..8ce6eff92 100644 --- a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java @@ -33,17 +33,30 @@ public class ExecutorUtils { private static final String TAG = "ExecutorUtils"; - private static ScheduledExecutorService sExecutorService = - Executors.newSingleThreadScheduledExecutor(new ExecutorFactory()); + public static final String KEYBOARD = "Keyboard"; + public static final String SPELLING = "Spelling"; + + private static ScheduledExecutorService sKeyboardExecutorService = newExecutorService(KEYBOARD); + private static ScheduledExecutorService sSpellingExecutorService = newExecutorService(SPELLING); + + private static ScheduledExecutorService newExecutorService(final String name) { + return Executors.newSingleThreadScheduledExecutor(new ExecutorFactory(name)); + } private static class ExecutorFactory implements ThreadFactory { + private final String mName; + + private ExecutorFactory(final String name) { + mName = name; + } + @Override public Thread newThread(final Runnable runnable) { Thread thread = new Thread(runnable, TAG); thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread thread, Throwable ex) { - Log.w(TAG + "-" + runnable.getClass().getSimpleName(), ex); + Log.w(mName + "-" + runnable.getClass().getSimpleName(), ex); } }); return thread; @@ -64,24 +77,44 @@ public class ExecutorUtils { // /** + * @param name Executor's name. * @return scheduled executor service used to run background tasks */ - public static ScheduledExecutorService getBackgroundExecutor() { + public static ScheduledExecutorService getBackgroundExecutor(final String name) { if (sExecutorServiceForTests != null) { return sExecutorServiceForTests; } - return sExecutorService; + switch (name) { + case KEYBOARD: + return sKeyboardExecutorService; + case SPELLING: + return sSpellingExecutorService; + default: + throw new IllegalArgumentException("Invalid executor: " + name); + } } - public static void killTasks() { - getBackgroundExecutor().shutdownNow(); + public static void killTasks(final String name) { + final ScheduledExecutorService executorService = getBackgroundExecutor(name); + executorService.shutdownNow(); try { - getBackgroundExecutor().awaitTermination(5, TimeUnit.SECONDS); + executorService.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { - Log.wtf(TAG, "Failed to shut down background task."); - throw new IllegalStateException("Failed to shut down background task."); - } finally { - sExecutorService = Executors.newSingleThreadScheduledExecutor(new ExecutorFactory()); + Log.wtf(TAG, "Failed to shut down: " + name); + } + if (executorService == sExecutorServiceForTests) { + // Don't do anything to the test service. + return; + } + switch (name) { + case KEYBOARD: + sKeyboardExecutorService = newExecutorService(KEYBOARD); + break; + case SPELLING: + sSpellingExecutorService = newExecutorService(SPELLING); + break; + default: + throw new IllegalArgumentException("Invalid executor: " + name); } } diff --git a/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java index d722151cf..86923059b 100644 --- a/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java @@ -35,7 +35,8 @@ public class ExecutorUtilsTests extends AndroidTestCase { private static final int DELAY_FOR_WAITING_TASKS_MILLISECONDS = 500; public void testExecute() { - final ExecutorService executor = ExecutorUtils.getBackgroundExecutor(); + final ExecutorService executor = + ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD); final AtomicInteger v = new AtomicInteger(0); for (int i = 0; i < NUM_OF_TASKS; ++i) { executor.execute(new Runnable() {