• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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