View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.common.future;
21  
22  import org.waarp.common.logging.SysErrLogger;
23  
24  import java.util.concurrent.TimeUnit;
25  
26  import static java.util.concurrent.TimeUnit.*;
27  
28  /**
29   * Ftp Future operation<br>
30   * Completely inspired from the excellent ChannelFuture of Netty, but without
31   * any channel inside.
32   */
33  public class WaarpFuture implements WaarpFutureInterface {
34    private static final Throwable CANCELLED = new Throwable();
35  
36    private final boolean cancellable;
37  
38    private volatile boolean done;
39  
40    private Throwable cause;
41  
42    private int waiters;
43  
44    /**
45     * Creates a new instance.
46     */
47    public WaarpFuture() {
48      cancellable = false;
49    }
50  
51    /**
52     * Creates a new instance.
53     *
54     * @param cancellable {@code true} if and only if this future can be
55     *     canceled
56     */
57    public WaarpFuture(final boolean cancellable) {
58      this.cancellable = cancellable;
59    }
60  
61    /**
62     * Returns {@code true} if and only if this future is complete, regardless
63     * of
64     * whether the operation was
65     * successful, failed, or canceled.
66     *
67     * @return True if the future is complete
68     */
69    @Override
70    public final boolean isDone() {
71      return done;
72    }
73  
74    /**
75     * Returns {@code true} if and only if the operation was completed
76     * successfully.
77     *
78     * @return True if the future is successful
79     */
80    @Override
81    public final boolean isSuccess() {
82      return done && cause == null;
83    }
84  
85    /**
86     * Returns {@code true} if and only if the operation was completed but
87     * unsuccessfully.
88     *
89     * @return True if the future is done but unsuccessful
90     */
91    @Override
92    public final boolean isFailed() {
93      return done && cause != null;
94    }
95  
96    /**
97     * Returns the cause of the failed operation if the operation has failed.
98     *
99     * @return the cause of the failure. {@code null} if succeeded or this
100    *     future
101    *     is not completed yet.
102    */
103   @Override
104   public final Throwable getCause() {
105     if (cause != CANCELLED) {
106       return cause;
107     }
108     return null;
109   }
110 
111   /**
112    * Returns {@code true} if and only if this future was canceled by a {@link
113    * #cancel()} method.
114    *
115    * @return True if the future was canceled
116    */
117   @Override
118   public final boolean isCancelled() {
119     return done && cause == CANCELLED;
120   }
121 
122   /**
123    * Rethrows the exception that caused this future fail if this future is
124    * complete and failed.
125    */
126   @Override
127   public final WaarpFuture rethrowIfFailed() throws Exception {
128     if (!isDone()) {
129       return this;
130     }
131 
132     final Throwable causeNew = getCause();
133     if (causeNew == null) {
134       return this;
135     }
136 
137     if (causeNew instanceof Exception) {
138       throw (Exception) causeNew;
139     }
140 
141     if (causeNew instanceof Error) {
142       throw (Error) causeNew;
143     }
144 
145     throw new RuntimeException(causeNew);
146   }
147 
148   /**
149    * @return True if the Future is done or False if interrupted
150    */
151   @Override
152   public final boolean awaitOrInterruptible() {
153     while (!Thread.interrupted()) {
154       if (awaitOrInterruptible(1, SECONDS)) {
155         return true;
156       }
157     }
158     return false;
159   }
160 
161   /**
162    * @param timeoutMilliseconds
163    *
164    * @return True if the Future is done or False if interrupted
165    */
166   @Override
167   public final boolean awaitOrInterruptible(final long timeoutMilliseconds) {
168     return awaitOrInterruptible(MILLISECONDS.toNanos(timeoutMilliseconds),
169                                 false);
170   }
171 
172   /**
173    * @param timeout
174    * @param unit
175    *
176    * @return True if the Future is done or False if interrupted
177    */
178   @Override
179   public final boolean awaitOrInterruptible(final long timeout,
180                                             final TimeUnit unit) {
181     return awaitOrInterruptible(unit.toNanos(timeout), false);
182   }
183 
184   /**
185    * @param timeoutNanos
186    * @param interruptable
187    *
188    * @return True if the Future is done or False if interrupted
189    */
190   private boolean awaitOrInterruptible(final long timeoutNanos,
191                                        final boolean interruptable) {
192     try {
193       if (await0(timeoutNanos, interruptable) && !Thread.interrupted()) {
194         return true;
195       }
196     } catch (final InterruptedException e) {//NOSONAR
197       SysErrLogger.FAKE_LOGGER.ignoreLog(e);
198     }
199     return false;
200   }
201 
202   private boolean await0(final long timeoutNanos, final boolean interruptable)
203       throws InterruptedException {
204     if (done) {
205       return done;
206     }
207     if (timeoutNanos <= 0) {
208       return done;
209     }
210     if (interruptable && Thread.interrupted()) {
211       throw new InterruptedException();
212     }
213 
214     final long startTime = System.nanoTime();
215     long waitTime = timeoutNanos;
216     boolean interrupted = false;
217 
218     try {
219       for (; ; ) {
220         synchronized (this) {
221           if (done) {
222             return done;
223           }
224           waiters++;
225 
226           try {
227             wait(waitTime / 1000000, (int) (waitTime % 1000000));
228           } catch (final InterruptedException e) {//NOSONAR
229             SysErrLogger.FAKE_LOGGER.ignoreLog(e);
230             if (interruptable) {
231               throw e;
232             } else {
233               interrupted = true;
234             }
235           } finally {
236             waiters--;
237           }
238         }
239         if (done) {
240           return true;
241         }
242         waitTime = timeoutNanos - (System.nanoTime() - startTime);
243         if (waitTime <= 0) {
244           return done;
245         }
246       }
247     } finally {
248       if (interrupted) {
249         Thread.currentThread().interrupt();
250       }
251     }
252   }
253 
254   /**
255    * Marks this future as a success and notifies all listeners.
256    *
257    * @return {@code true} if and only if successfully marked this future as a
258    *     success. Otherwise {@code false}
259    *     because this future is already marked as either a success or a
260    *     failure.
261    */
262   @Override
263   public final boolean setSuccess() {
264     synchronized (this) {
265       // Allow only once.
266       if (done) {
267         return false;
268       }
269 
270       done = true;
271       if (waiters > 0) {
272         notifyAll();
273       }
274     }
275     return true;
276   }
277 
278   /**
279    * Marks this future as a failure and notifies all listeners.
280    *
281    * @param cause
282    *
283    * @return {@code true} if and only if successfully marked this future as a
284    *     failure. Otherwise {@code false}
285    *     because this future is already marked as either a success or a
286    *     failure.
287    */
288   @Override
289   public final boolean setFailure(final Throwable cause) {
290     synchronized (this) {
291       // Allow only once.
292       if (done) {
293         return false;
294       }
295 
296       this.cause = cause;
297       done = true;
298       if (waiters > 0) {
299         notifyAll();
300       }
301     }
302     return true;
303   }
304 
305   /**
306    * Cancels the operation associated with this future and notifies all
307    * listeners if canceled successfully.
308    *
309    * @return {@code true} if and only if the operation has been canceled.
310    *     {@code
311    *     false} if the operation can't
312    *     be canceled or is already completed.
313    */
314   @Override
315   public final boolean cancel() {
316     if (!cancellable) {
317       return false;
318     }
319     synchronized (this) {
320       // Allow only once.
321       if (done) {
322         return false;
323       }
324 
325       cause = CANCELLED;
326       done = true;
327       if (waiters > 0) {
328         notifyAll();
329       }
330     }
331     return true;
332   }
333 
334   /**
335    * Experimental: try to re-enable the future
336    */
337   @Override
338   public final void reset() {
339     synchronized (this) {
340       done = false;
341       cause = null;
342     }
343   }
344 }