All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.util.concurrent.SequentialExecutor Maven / Gradle / Ivy

Go to download

Guava is a suite of core and expanded libraries that include utility classes, google's collections, io classes, and much much more.

There is a newer version: 33.1.0-jre
Show newest version
/*
 * Copyright (C) 2008 The Guava Authors
 *
 * 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.google.common.util.concurrent;

import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Preconditions;
import com.google.j2objc.annotations.WeakOuter;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;

/**
 * Executor ensuring that all Runnables submitted are executed in order, using the provided
 * Executor, and sequentially such that no two will ever be running at the same time.
 *
 * 

Tasks submitted to {@link #execute(Runnable)} are executed in FIFO order. * *

The execution of tasks is done by one thread as long as there are tasks left in the queue. * When a task is {@linkplain Thread#interrupt interrupted}, execution of subsequent tasks * continues. See {@link QueueWorker#workOnQueue} for details. * *

{@code RuntimeException}s thrown by tasks are simply logged and the executor keeps trucking. * If an {@code Error} is thrown, the error will propagate and execution will stop until it is * restarted by a call to {@link #execute}. */ @GwtIncompatible final class SequentialExecutor implements Executor { private static final Logger log = Logger.getLogger(SequentialExecutor.class.getName()); /** Underlying executor that all submitted Runnable objects are run on. */ private final Executor executor; @GuardedBy("queue") private final Queue queue = new ArrayDeque<>(); @GuardedBy("queue") private boolean isWorkerRunning = false; private final QueueWorker worker = new QueueWorker(); /** Use {@link MoreExecutors#newSequentialExecutor} */ SequentialExecutor(Executor executor) { this.executor = Preconditions.checkNotNull(executor); } /** * Adds a task to the queue and makes sure a worker thread is running. * *

If this method throws, e.g. a {@code RejectedExecutionException} from the delegate executor, * execution of tasks will stop until a call to this method or to {@link #resume()} is made. */ @Override public void execute(Runnable task) { synchronized (queue) { queue.add(task); if (isWorkerRunning) { return; } isWorkerRunning = true; } startQueueWorker(); } /** * Starts a worker. This should only be called if: * *

    *
  • {@code suspensions == 0} *
  • {@code isWorkerRunning == true} *
  • {@code !queue.isEmpty()} *
  • the {@link #worker} lock is not held *
*/ private void startQueueWorker() { boolean executionRejected = true; try { executor.execute(worker); executionRejected = false; } finally { if (executionRejected) { // The best we can do is to stop executing the queue, but reset the state so that // execution can be resumed later if the caller so wishes. synchronized (queue) { isWorkerRunning = false; } } } } /** Worker that runs tasks from {@link #queue} until it is empty. */ @WeakOuter private final class QueueWorker implements Runnable { @Override public void run() { try { workOnQueue(); } catch (Error e) { synchronized (queue) { isWorkerRunning = false; } throw e; // The execution of a task has ended abnormally. // We could have tasks left in the queue, so should perhaps try to restart a worker, // but then the Error will get delayed if we are using a direct (same thread) executor. } } /** * Continues executing tasks from {@link #queue} until it is empty. * *

The thread's interrupt bit is cleared before execution of each task. * *

If the Thread in use is interrupted before or during execution of the tasks in * {@link #queue}, the Executor will complete its tasks, and then restore the interruption. * This means that once the Thread returns to the Executor that this Executor composes, the * interruption will still be present. If the composed Executor is an ExecutorService, it can * respond to shutdown() by returning tasks queued on that Thread after {@link #worker} drains * the queue. */ private void workOnQueue() { boolean interruptedDuringTask = false; try { while (true) { // Remove the interrupt bit before each task. The interrupt is for the "current task" when // it is sent, so subsequent tasks in the queue should not be caused to be interrupted // by a previous one in the queue being interrupted. interruptedDuringTask |= Thread.interrupted(); Runnable task; synchronized (queue) { task = queue.poll(); if (task == null) { isWorkerRunning = false; return; } } try { task.run(); } catch (RuntimeException e) { log.log(Level.SEVERE, "Exception while executing runnable " + task, e); } } } finally { // Ensure that if the thread was interrupted at all while processing the task queue, it // is returned to the delegate Executor interrupted so that it may handle the // interruption if it likes. if (interruptedDuringTask) { Thread.currentThread().interrupt(); } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy