Loading java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java +25 −6 Original line number Diff line number Diff line Loading @@ -19,25 +19,44 @@ package com.android.inputmethod.latin.utils; import com.android.inputmethod.annotations.UsedForTesting; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; /** * Utilities to manage executors. */ public class ExecutorUtils { private static final ConcurrentHashMap<String, PrioritizedSerialExecutor> sExecutorMap = private static final ConcurrentHashMap<String, ExecutorService> sExecutorMap = new ConcurrentHashMap<>(); private static class ThreadFactoryWithId implements ThreadFactory { private final String mId; public ThreadFactoryWithId(final String id) { mId = id; } @Override public Thread newThread(final Runnable r) { return new Thread(r, "Executor - " + mId); } } /** * Gets the executor for the given id. */ public static PrioritizedSerialExecutor getExecutor(final String id) { PrioritizedSerialExecutor executor = sExecutorMap.get(id); public static ExecutorService getExecutor(final String id) { ExecutorService executor = sExecutorMap.get(id); if (executor == null) { synchronized(sExecutorMap) { executor = new PrioritizedSerialExecutor(id); executor = sExecutorMap.get(id); if (executor == null) { executor = Executors.newSingleThreadExecutor(new ThreadFactoryWithId(id)); sExecutorMap.put(id, executor); } } } return executor; } Loading @@ -47,7 +66,7 @@ public class ExecutorUtils { @UsedForTesting public static void shutdownAllExecutors() { synchronized(sExecutorMap) { for (final PrioritizedSerialExecutor executor : sExecutorMap.values()) { for (final ExecutorService executor : sExecutorMap.values()) { executor.execute(new Runnable() { @Override public void run() { Loading java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.javadeleted 100644 → 0 +0 −137 Original line number Diff line number Diff line /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.inputmethod.latin.utils; import com.android.inputmethod.annotations.UsedForTesting; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * An object that executes submitted tasks using a thread. */ public class PrioritizedSerialExecutor { public static final String TAG = PrioritizedSerialExecutor.class.getSimpleName(); private final Object mLock = new Object(); private final Queue<Runnable> mTasks; private final Queue<Runnable> mPrioritizedTasks; private boolean mIsShutdown; private final ThreadPoolExecutor mThreadPoolExecutor; // The task which is running now. private Runnable mActive; private static class ThreadFactoryWithId implements ThreadFactory { private final String mId; public ThreadFactoryWithId(final String id) { mId = id; } @Override public Thread newThread(final Runnable r) { return new Thread(r, TAG + " - " + mId); } } public PrioritizedSerialExecutor(final String id) { mTasks = new ConcurrentLinkedQueue<>(); mPrioritizedTasks = new ConcurrentLinkedQueue<>(); mIsShutdown = false; mThreadPoolExecutor = new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */, 0 /* keepAliveTime */, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), new ThreadFactoryWithId(id)); } /** * Enqueues the given task into the task queue. * @param r the enqueued task */ public void execute(final Runnable r) { synchronized(mLock) { if (!mIsShutdown) { mTasks.offer(new Runnable() { @Override public void run() { try { r.run(); } finally { scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } } } } /** * Enqueues the given task into the prioritized task queue. * @param r the enqueued task */ @UsedForTesting public void executePrioritized(final Runnable r) { synchronized(mLock) { if (!mIsShutdown) { mPrioritizedTasks.offer(new Runnable() { @Override public void run() { try { r.run(); } finally { scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } } } } private boolean fetchNextTasksLocked() { mActive = mPrioritizedTasks.poll(); if (mActive == null) { mActive = mTasks.poll(); } return mActive != null; } private void scheduleNext() { synchronized(mLock) { if (fetchNextTasksLocked()) { mThreadPoolExecutor.execute(mActive); } } } public void shutdown() { synchronized(mLock) { mIsShutdown = true; mThreadPoolExecutor.shutdown(); } } } tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java→tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java +57 −0 Original line number Diff line number Diff line Loading @@ -20,22 +20,23 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Unit tests for PrioritizedSerialExecutor. * TODO: Add more detailed tests to make use of priorities, etc. * Unit tests for ExecutorUtils. */ @MediumTest public class PrioritizedSerialExecutorTests extends AndroidTestCase { private static final String TAG = PrioritizedSerialExecutorTests.class.getSimpleName(); public class ExecutorUtilsTests extends AndroidTestCase { private static final String TAG = ExecutorUtilsTests.class.getSimpleName(); private static final String TEST_EXECUTOR_ID = "test"; private static final int NUM_OF_TASKS = 10; private static final int DELAY_FOR_WAITING_TASKS_MILLISECONDS = 500; public void testExecute() { final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(TEST_EXECUTOR_ID); final ExecutorService executor = ExecutorUtils.getExecutor(TEST_EXECUTOR_ID); final AtomicInteger v = new AtomicInteger(0); for (int i = 0; i < NUM_OF_TASKS; ++i) { executor.execute(new Runnable() { Loading @@ -46,61 +47,11 @@ public class PrioritizedSerialExecutorTests extends AndroidTestCase { }); } try { Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); executor.awaitTermination(DELAY_FOR_WAITING_TASKS_MILLISECONDS, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Log.d(TAG, "Exception while sleeping.", e); } assertEquals(NUM_OF_TASKS, v.get()); } public void testExecutePrioritized() { final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(TEST_EXECUTOR_ID); final AtomicInteger v = new AtomicInteger(0); for (int i = 0; i < NUM_OF_TASKS; ++i) { executor.executePrioritized(new Runnable() { @Override public void run() { v.incrementAndGet(); } }); } try { Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); } catch (InterruptedException e) { Log.d(TAG, "Exception while sleeping.", e); } assertEquals(NUM_OF_TASKS, v.get()); } public void testExecuteCombined() { final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(TEST_EXECUTOR_ID); final AtomicInteger v = new AtomicInteger(0); for (int i = 0; i < NUM_OF_TASKS; ++i) { executor.execute(new Runnable() { @Override public void run() { v.incrementAndGet(); } }); } for (int i = 0; i < NUM_OF_TASKS; ++i) { executor.executePrioritized(new Runnable() { @Override public void run() { v.incrementAndGet(); } }); } try { Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); } catch (InterruptedException e) { Log.d(TAG, "Exception while sleeping.", e); } assertEquals(2 * NUM_OF_TASKS, v.get()); } } Loading
java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java +25 −6 Original line number Diff line number Diff line Loading @@ -19,25 +19,44 @@ package com.android.inputmethod.latin.utils; import com.android.inputmethod.annotations.UsedForTesting; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; /** * Utilities to manage executors. */ public class ExecutorUtils { private static final ConcurrentHashMap<String, PrioritizedSerialExecutor> sExecutorMap = private static final ConcurrentHashMap<String, ExecutorService> sExecutorMap = new ConcurrentHashMap<>(); private static class ThreadFactoryWithId implements ThreadFactory { private final String mId; public ThreadFactoryWithId(final String id) { mId = id; } @Override public Thread newThread(final Runnable r) { return new Thread(r, "Executor - " + mId); } } /** * Gets the executor for the given id. */ public static PrioritizedSerialExecutor getExecutor(final String id) { PrioritizedSerialExecutor executor = sExecutorMap.get(id); public static ExecutorService getExecutor(final String id) { ExecutorService executor = sExecutorMap.get(id); if (executor == null) { synchronized(sExecutorMap) { executor = new PrioritizedSerialExecutor(id); executor = sExecutorMap.get(id); if (executor == null) { executor = Executors.newSingleThreadExecutor(new ThreadFactoryWithId(id)); sExecutorMap.put(id, executor); } } } return executor; } Loading @@ -47,7 +66,7 @@ public class ExecutorUtils { @UsedForTesting public static void shutdownAllExecutors() { synchronized(sExecutorMap) { for (final PrioritizedSerialExecutor executor : sExecutorMap.values()) { for (final ExecutorService executor : sExecutorMap.values()) { executor.execute(new Runnable() { @Override public void run() { Loading
java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.javadeleted 100644 → 0 +0 −137 Original line number Diff line number Diff line /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.inputmethod.latin.utils; import com.android.inputmethod.annotations.UsedForTesting; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * An object that executes submitted tasks using a thread. */ public class PrioritizedSerialExecutor { public static final String TAG = PrioritizedSerialExecutor.class.getSimpleName(); private final Object mLock = new Object(); private final Queue<Runnable> mTasks; private final Queue<Runnable> mPrioritizedTasks; private boolean mIsShutdown; private final ThreadPoolExecutor mThreadPoolExecutor; // The task which is running now. private Runnable mActive; private static class ThreadFactoryWithId implements ThreadFactory { private final String mId; public ThreadFactoryWithId(final String id) { mId = id; } @Override public Thread newThread(final Runnable r) { return new Thread(r, TAG + " - " + mId); } } public PrioritizedSerialExecutor(final String id) { mTasks = new ConcurrentLinkedQueue<>(); mPrioritizedTasks = new ConcurrentLinkedQueue<>(); mIsShutdown = false; mThreadPoolExecutor = new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */, 0 /* keepAliveTime */, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), new ThreadFactoryWithId(id)); } /** * Enqueues the given task into the task queue. * @param r the enqueued task */ public void execute(final Runnable r) { synchronized(mLock) { if (!mIsShutdown) { mTasks.offer(new Runnable() { @Override public void run() { try { r.run(); } finally { scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } } } } /** * Enqueues the given task into the prioritized task queue. * @param r the enqueued task */ @UsedForTesting public void executePrioritized(final Runnable r) { synchronized(mLock) { if (!mIsShutdown) { mPrioritizedTasks.offer(new Runnable() { @Override public void run() { try { r.run(); } finally { scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } } } } private boolean fetchNextTasksLocked() { mActive = mPrioritizedTasks.poll(); if (mActive == null) { mActive = mTasks.poll(); } return mActive != null; } private void scheduleNext() { synchronized(mLock) { if (fetchNextTasksLocked()) { mThreadPoolExecutor.execute(mActive); } } } public void shutdown() { synchronized(mLock) { mIsShutdown = true; mThreadPoolExecutor.shutdown(); } } }
tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java→tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java +57 −0 Original line number Diff line number Diff line Loading @@ -20,22 +20,23 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Unit tests for PrioritizedSerialExecutor. * TODO: Add more detailed tests to make use of priorities, etc. * Unit tests for ExecutorUtils. */ @MediumTest public class PrioritizedSerialExecutorTests extends AndroidTestCase { private static final String TAG = PrioritizedSerialExecutorTests.class.getSimpleName(); public class ExecutorUtilsTests extends AndroidTestCase { private static final String TAG = ExecutorUtilsTests.class.getSimpleName(); private static final String TEST_EXECUTOR_ID = "test"; private static final int NUM_OF_TASKS = 10; private static final int DELAY_FOR_WAITING_TASKS_MILLISECONDS = 500; public void testExecute() { final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(TEST_EXECUTOR_ID); final ExecutorService executor = ExecutorUtils.getExecutor(TEST_EXECUTOR_ID); final AtomicInteger v = new AtomicInteger(0); for (int i = 0; i < NUM_OF_TASKS; ++i) { executor.execute(new Runnable() { Loading @@ -46,61 +47,11 @@ public class PrioritizedSerialExecutorTests extends AndroidTestCase { }); } try { Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); executor.awaitTermination(DELAY_FOR_WAITING_TASKS_MILLISECONDS, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Log.d(TAG, "Exception while sleeping.", e); } assertEquals(NUM_OF_TASKS, v.get()); } public void testExecutePrioritized() { final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(TEST_EXECUTOR_ID); final AtomicInteger v = new AtomicInteger(0); for (int i = 0; i < NUM_OF_TASKS; ++i) { executor.executePrioritized(new Runnable() { @Override public void run() { v.incrementAndGet(); } }); } try { Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); } catch (InterruptedException e) { Log.d(TAG, "Exception while sleeping.", e); } assertEquals(NUM_OF_TASKS, v.get()); } public void testExecuteCombined() { final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(TEST_EXECUTOR_ID); final AtomicInteger v = new AtomicInteger(0); for (int i = 0; i < NUM_OF_TASKS; ++i) { executor.execute(new Runnable() { @Override public void run() { v.incrementAndGet(); } }); } for (int i = 0; i < NUM_OF_TASKS; ++i) { executor.executePrioritized(new Runnable() { @Override public void run() { v.incrementAndGet(); } }); } try { Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); } catch (InterruptedException e) { Log.d(TAG, "Exception while sleeping.", e); } assertEquals(2 * NUM_OF_TASKS, v.get()); } }