Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c4a3b9f0 authored by Keisuke Kuroyanagi's avatar Keisuke Kuroyanagi Committed by Android (Google) Code Review
Browse files

Merge "Remove PrioritizedSerialExecutor."

parents 944923f2 94f88b48
Loading
Loading
Loading
Loading
+25 −6
Original line number Diff line number Diff line
@@ -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;
    }

@@ -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() {
+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();
        }
    }
}
+57 −0
Original line number Diff line number Diff line
@@ -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() {
@@ -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());
    }
}