View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.core5.concurrent;
28  
29  import java.util.concurrent.CancellationException;
30  import java.util.concurrent.ExecutionException;
31  import java.util.concurrent.Future;
32  import java.util.concurrent.TimeUnit;
33  import java.util.concurrent.TimeoutException;
34  import java.util.concurrent.locks.Condition;
35  import java.util.concurrent.locks.ReentrantLock;
36  
37  import org.apache.hc.core5.util.Args;
38  import org.apache.hc.core5.util.TimeoutValueException;
39  
40  /**
41   * Basic implementation of the {@link Future} interface. {@code BasicFuture}
42   * can be put into a completed state by invoking any of the following methods:
43   * {@link #cancel()}, {@link #failed(Exception)}, or {@link #completed(Object)}.
44   *
45   * @param <T> the future result type of an asynchronous operation.
46   * @since 4.2
47   */
48  public class BasicFuture<T> implements Future<T>, Cancellable {
49  
50      private final FutureCallback<T> callback;
51  
52      private volatile boolean completed;
53      private volatile boolean cancelled;
54      private volatile T result;
55      private volatile Exception ex;
56  
57      private final ReentrantLock lock;
58      private final Condition condition;
59  
60      public BasicFuture(final FutureCallback<T> callback) {
61          super();
62          this.callback = callback;
63          this.lock = new ReentrantLock();
64          this.condition = lock.newCondition();
65      }
66  
67      @Override
68      public boolean isCancelled() {
69          return this.cancelled;
70      }
71  
72      @Override
73      public boolean isDone() {
74          return this.completed;
75      }
76  
77      private T getResult() throws ExecutionException {
78          if (this.ex != null) {
79              throw new ExecutionException(this.ex);
80          }
81          if (cancelled) {
82              throw new CancellationException();
83          }
84          return this.result;
85      }
86  
87      @Override
88      public T get() throws InterruptedException, ExecutionException {
89          lock.lock();
90          try {
91              while (!this.completed) {
92                  condition.await();
93              }
94              return getResult();
95          } finally {
96              lock.unlock();
97          }
98      }
99  
100     @Override
101     public T get(final long timeout, final TimeUnit unit)
102             throws InterruptedException, ExecutionException, TimeoutException {
103         Args.notNull(unit, "Time unit");
104         final long msecs = unit.toMillis(timeout);
105         final long startTime = (msecs <= 0) ? 0 : System.currentTimeMillis();
106         long waitTime = msecs;
107         try {
108             lock.lock();
109             if (this.completed) {
110                 return getResult();
111             } else if (waitTime <= 0) {
112                 throw TimeoutValueException.fromMilliseconds(msecs, msecs + Math.abs(waitTime));
113             } else {
114                 for (; ; ) {
115                     condition.await(waitTime, TimeUnit.MILLISECONDS);
116                     if (this.completed) {
117                         return getResult();
118                     }
119                     waitTime = msecs - (System.currentTimeMillis() - startTime);
120                     if (waitTime <= 0) {
121                         throw TimeoutValueException.fromMilliseconds(msecs, msecs + Math.abs(waitTime));
122                     }
123                 }
124             }
125         } finally {
126             lock.unlock();
127         }
128     }
129 
130     public boolean completed(final T result) {
131         lock.lock();
132         try {
133             if (this.completed) {
134                 return false;
135             }
136             this.completed = true;
137             this.result = result;
138             condition.signalAll();
139         } finally {
140             lock.unlock();
141         }
142         if (this.callback != null) {
143             this.callback.completed(result);
144         }
145         return true;
146     }
147 
148     public boolean failed(final Exception exception) {
149         lock.lock();
150         try {
151             if (this.completed) {
152                 return false;
153             }
154             this.completed = true;
155             this.ex = exception;
156             condition.signalAll();
157         } finally {
158             lock.unlock();
159         }
160         if (this.callback != null) {
161             this.callback.failed(exception);
162         }
163         return true;
164     }
165 
166     @Override
167     public boolean cancel(final boolean mayInterruptIfRunning) {
168         lock.lock();
169         try {
170             if (this.completed) {
171                 return false;
172             }
173             this.completed = true;
174             this.cancelled = true;
175             condition.signalAll();
176         } finally {
177             lock.unlock();
178         }
179         if (this.callback != null) {
180             this.callback.cancelled();
181         }
182         return true;
183     }
184 
185     @Override
186     public boolean cancel() {
187         return cancel(true);
188     }
189 
190 }