1 /* 2 * Copyright (C) 2018 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.google.common.util.concurrent; 16 17 import static com.google.common.truth.Truth.assertThat; 18 import static com.google.common.util.concurrent.Futures.getDone; 19 import static com.google.common.util.concurrent.MoreExecutors.directExecutor; 20 21 import java.util.concurrent.Callable; 22 import java.util.concurrent.CountDownLatch; 23 import java.util.concurrent.ExecutorService; 24 import java.util.concurrent.Executors; 25 import java.util.concurrent.Future; 26 import java.util.concurrent.TimeUnit; 27 import junit.framework.TestCase; 28 29 /** Tests for {@link ExecutionSequencer} */ 30 public class ExecutionSequencerTest extends TestCase { 31 32 ExecutorService executor; 33 34 private ExecutionSequencer serializer; 35 private SettableFuture<Void> firstFuture; 36 private TestCallable firstCallable; 37 38 @Override setUp()39 public void setUp() throws Exception { 40 executor = Executors.newCachedThreadPool(); 41 serializer = ExecutionSequencer.create(); 42 firstFuture = SettableFuture.create(); 43 firstCallable = new TestCallable(firstFuture); 44 } 45 46 @Override tearDown()47 public void tearDown() throws Exception { 48 executor.shutdown(); 49 } 50 testCallableStartsAfterFirstFutureCompletes()51 public void testCallableStartsAfterFirstFutureCompletes() { 52 @SuppressWarnings({"unused", "nullness"}) 53 Future<?> possiblyIgnoredError = serializer.submitAsync(firstCallable, directExecutor()); 54 TestCallable secondCallable = new TestCallable(Futures.<Void>immediateFuture(null)); 55 @SuppressWarnings({"unused", "nullness"}) 56 Future<?> possiblyIgnoredError1 = serializer.submitAsync(secondCallable, directExecutor()); 57 assertThat(firstCallable.called).isTrue(); 58 assertThat(secondCallable.called).isFalse(); 59 firstFuture.set(null); 60 assertThat(secondCallable.called).isTrue(); 61 } 62 testCancellationNotPropagatedIfAlreadyStarted()63 public void testCancellationNotPropagatedIfAlreadyStarted() { 64 serializer.submitAsync(firstCallable, directExecutor()).cancel(true); 65 assertThat(firstFuture.isCancelled()).isFalse(); 66 } 67 testCancellationDoesNotViolateSerialization()68 public void testCancellationDoesNotViolateSerialization() { 69 @SuppressWarnings({"unused", "nullness"}) 70 Future<?> possiblyIgnoredError = serializer.submitAsync(firstCallable, directExecutor()); 71 TestCallable secondCallable = new TestCallable(Futures.<Void>immediateFuture(null)); 72 ListenableFuture<Void> secondFuture = serializer.submitAsync(secondCallable, directExecutor()); 73 TestCallable thirdCallable = new TestCallable(Futures.<Void>immediateFuture(null)); 74 @SuppressWarnings({"unused", "nullness"}) 75 Future<?> possiblyIgnoredError1 = serializer.submitAsync(thirdCallable, directExecutor()); 76 secondFuture.cancel(true); 77 assertThat(secondCallable.called).isFalse(); 78 assertThat(thirdCallable.called).isFalse(); 79 firstFuture.set(null); 80 assertThat(secondCallable.called).isFalse(); 81 assertThat(thirdCallable.called).isTrue(); 82 } 83 testCancellationMultipleThreads()84 public void testCancellationMultipleThreads() throws Exception { 85 final BlockingCallable blockingCallable = new BlockingCallable(); 86 ListenableFuture<Void> unused = serializer.submit(blockingCallable, executor); 87 ListenableFuture<Boolean> future2 = 88 serializer.submit( 89 new Callable<Boolean>() { 90 @Override 91 public Boolean call() { 92 return blockingCallable.isRunning(); 93 } 94 }, 95 directExecutor()); 96 97 // Wait for the first task to be started in the background. It will block until we explicitly 98 // stop it. 99 blockingCallable.waitForStart(); 100 101 // Give the second task a chance to (incorrectly) start up while the first task is running. 102 assertThat(future2.isDone()).isFalse(); 103 104 // Stop the first task. The second task should then run. 105 blockingCallable.stop(); 106 executor.shutdown(); 107 assertThat(executor.awaitTermination(10, TimeUnit.SECONDS)).isTrue(); 108 assertThat(getDone(future2)).isFalse(); 109 } 110 testSecondTaskWaitsForFirstEvenIfCancelled()111 public void testSecondTaskWaitsForFirstEvenIfCancelled() throws Exception { 112 final BlockingCallable blockingCallable = new BlockingCallable(); 113 ListenableFuture<Void> future1 = serializer.submit(blockingCallable, executor); 114 ListenableFuture<Boolean> future2 = 115 serializer.submit( 116 new Callable<Boolean>() { 117 @Override 118 public Boolean call() { 119 return blockingCallable.isRunning(); 120 } 121 }, 122 directExecutor()); 123 124 // Wait for the first task to be started in the background. It will block until we explicitly 125 // stop it. 126 blockingCallable.waitForStart(); 127 128 // This time, cancel the future for the first task. The task remains running, only the future 129 // is cancelled. 130 future1.cancel(false); 131 132 // Give the second task a chance to (incorrectly) start up while the first task is running. 133 // (This is the assertion that fails.) 134 assertThat(future2.isDone()).isFalse(); 135 136 // Stop the first task. The second task should then run. 137 blockingCallable.stop(); 138 executor.shutdown(); 139 assertThat(executor.awaitTermination(10, TimeUnit.SECONDS)).isTrue(); 140 assertThat(getDone(future2)).isFalse(); 141 } 142 testToString()143 public void testToString() { 144 Future<?> first = serializer.submitAsync(firstCallable, directExecutor()); 145 TestCallable secondCallable = new TestCallable(SettableFuture.<Void>create()); 146 Future<?> second = serializer.submitAsync(secondCallable, directExecutor()); 147 assertThat(secondCallable.called).isFalse(); 148 assertThat(second.toString()).contains(secondCallable.toString()); 149 firstFuture.set(null); 150 assertThat(second.toString()).contains(secondCallable.future.toString()); 151 } 152 153 private static class BlockingCallable implements Callable<Void> { 154 private final CountDownLatch startLatch = new CountDownLatch(1); 155 private final CountDownLatch stopLatch = new CountDownLatch(1); 156 157 private volatile boolean running = false; 158 159 @Override call()160 public Void call() throws InterruptedException { 161 running = true; 162 startLatch.countDown(); 163 stopLatch.await(); 164 running = false; 165 return null; 166 } 167 waitForStart()168 public void waitForStart() throws InterruptedException { 169 startLatch.await(); 170 } 171 stop()172 public void stop() { 173 stopLatch.countDown(); 174 } 175 isRunning()176 public boolean isRunning() { 177 return running; 178 } 179 } 180 181 private static final class TestCallable implements AsyncCallable<Void> { 182 183 private final ListenableFuture<Void> future; 184 private boolean called = false; 185 TestCallable(ListenableFuture<Void> future)186 private TestCallable(ListenableFuture<Void> future) { 187 this.future = future; 188 } 189 190 @Override call()191 public ListenableFuture<Void> call() throws Exception { 192 called = true; 193 return future; 194 } 195 } 196 } 197