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 }