/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.common.executors;
import javax.annotation.concurrent.GuardedBy;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Executor;
import com.facebook.common.internal.Preconditions;
import com.facebook.common.internal.VisibleForTesting;
/**
* Simple implementation of delegating Executor that limits concurrency of execution to single
* thread.
*/
public class SerialDelegatingExecutor implements Executor {
private final Executor mDelegate;
@VisibleForTesting
final Runnable mRunnable;
/**
* True if and only if runnable has been passed to mDelegate for execution, but the execution
* itself has not completed yet.
*/
@GuardedBy("this")
@VisibleForTesting
boolean mExecutionInProgress;
@GuardedBy("this")
final private Queue<Runnable> mCommands;
public SerialDelegatingExecutor(Executor delegate) {
mDelegate = Preconditions.checkNotNull(delegate);
mExecutionInProgress = false;
mCommands = new LinkedList<Runnable>();
mRunnable = new Runnable() {
@Override
public void run() {
executeSingleCommand();
}
};
}
/**
* Submits another command for execution
*/
@Override
public void execute(Runnable command) {
synchronized (this) {
mCommands.add(command);
}
maybeSubmitRunnable();
}
private void maybeSubmitRunnable() {
synchronized (this) {
if (mExecutionInProgress || mCommands.isEmpty()) {
return;
}
mExecutionInProgress = true;
}
mDelegate.execute(mRunnable);
}
private void executeSingleCommand() {
Runnable command;
try {
removeNextCommand().run();
} finally {
clearExecutionInProgress();
maybeSubmitRunnable();
}
}
private synchronized Runnable removeNextCommand() {
return mCommands.remove();
}
private synchronized void clearExecutionInProgress() {
mExecutionInProgress = false;
}
}