001 /* 002 * Copyright (C) 2007 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package com.google.common.util.concurrent; 018 019 import com.google.common.annotations.Beta; 020 import com.google.common.base.Preconditions; 021 import com.google.common.collect.Lists; 022 023 import java.util.Queue; 024 import java.util.concurrent.Executor; 025 import java.util.logging.Level; 026 import java.util.logging.Logger; 027 028 /** 029 * <p>A list of ({@code Runnable}, {@code Executor}) pairs that guarantees 030 * that every {@code Runnable} that is added using the add method will be 031 * executed in its associated {@code Executor} after {@link #run()} is called. 032 * {@code Runnable}s added after {@code run} is called are still guaranteed to 033 * execute. 034 * 035 * @author Nishant Thakkar 036 * @author Sven Mawson 037 * @since 1 038 */ 039 @Beta 040 public final class ExecutionList implements Runnable { 041 042 // Logger to log exceptions caught when running runnables. 043 private static final Logger log = 044 Logger.getLogger(ExecutionList.class.getName()); 045 046 // The runnable,executor pairs to execute. 047 private final Queue<RunnableExecutorPair> runnables = Lists.newLinkedList(); 048 049 // Boolean we use mark when execution has started. Only accessed from within 050 // synchronized blocks. 051 private boolean executed = false; 052 053 /** 054 * Add the runnable/executor pair to the list of pairs to execute. Executes 055 * the pair immediately if we've already started execution. 056 */ 057 public void add(Runnable runnable, Executor executor) { 058 059 // Fail fast on a null. We throw NPE here because the contract of 060 // Executor states that it throws NPE on null listener, so we propagate 061 // that contract up into the add method as well. 062 Preconditions.checkNotNull(runnable, "Runnable was null."); 063 Preconditions.checkNotNull(executor, "Executor was null."); 064 065 boolean executeImmediate = false; 066 067 // Lock while we check state. We must maintain the lock while adding the 068 // new pair so that another thread can't run the list out from under us. 069 // We only add to the list if we have not yet started execution. 070 synchronized (runnables) { 071 if (!executed) { 072 runnables.add(new RunnableExecutorPair(runnable, executor)); 073 } else { 074 executeImmediate = true; 075 } 076 } 077 078 // Execute the runnable immediately. Because of scheduling this may end up 079 // getting called before some of the previously added runnables, but we're 080 // ok with that. If we want to change the contract to guarantee ordering 081 // among runnables we'd have to modify the logic here to allow it. 082 if (executeImmediate) { 083 executor.execute(runnable); 084 } 085 } 086 087 /** 088 * Runs this execution list, executing all pairs in the order they were 089 * added. Pairs added after this method has started executing the list will 090 * be executed immediately. 091 */ 092 @Override 093 public void run() { 094 095 // Lock while we update our state so the add method above will finish adding 096 // any listeners before we start to run them. 097 synchronized (runnables) { 098 executed = true; 099 } 100 101 // At this point the runnables will never be modified by another 102 // thread, so we are safe using it outside of the synchronized block. 103 while (!runnables.isEmpty()) { 104 runnables.poll().execute(); 105 } 106 } 107 108 private static class RunnableExecutorPair { 109 final Runnable runnable; 110 final Executor executor; 111 112 RunnableExecutorPair(Runnable runnable, Executor executor) { 113 this.runnable = runnable; 114 this.executor = executor; 115 } 116 117 void execute() { 118 try { 119 executor.execute(runnable); 120 } catch (RuntimeException e) { 121 // Log it and keep going, bad runnable and/or executor. Don't 122 // punish the other runnables if we're given a bad one. We only 123 // catch RuntimeException because we want Errors to propagate up. 124 log.log(Level.SEVERE, "RuntimeException while executing runnable " 125 + runnable + " with executor " + executor, e); 126 } 127 } 128 } 129 }