]> git.basschouten.com Git - openhab-addons.git/blob
efc99e7b80992bf9bd1a1ab2b388d105cdc5f3b7
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.bluetooth.util;
14
15 import java.util.concurrent.CancellationException;
16 import java.util.concurrent.CompletableFuture;
17 import java.util.concurrent.CompletionStage;
18 import java.util.concurrent.Executor;
19 import java.util.concurrent.Future;
20 import java.util.function.Function;
21 import java.util.function.Supplier;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25
26 /**
27  * The {@code HeritableFuture} class extends {@link CompletableFuture} and adds the ability
28  * to cancel upstream CompletableFuture tasks. Normally when a CompletableFuture
29  * is cancelled only dependent futures cancel. This class will also cancel the parent
30  * HeritableFuture instances as well. All of the {@code CompletionStage} methods will
31  * return HeritableFuture children and thus by only maintaining a reference to the final future
32  * in the task chain it would be possible to cancel the entire chain by calling {@code cancel}.
33  * <p>
34  * Due to child futures now having a link to their parent futures, it is no longer possible
35  * for HeritableFuture to be garbage collected as upstream futures complete. It is highly
36  * advisable to only use a HeritableFuture for defining finite (preferably small) task trees. Do not use
37  * HeritableFuture in situations where you would endlessly append new tasks otherwise you will eventually
38  * cause an OutOfMemoryError.
39  *
40  * @author Connor Petty - Initial contribution
41  *
42  */
43 @NonNullByDefault
44 public class HeritableFuture<T> extends CompletableFuture<T> {
45
46     protected final Object futureLock = new Object();
47     protected @Nullable Future<?> parentFuture;
48
49     public HeritableFuture() {
50     }
51
52     public HeritableFuture(Future<?> parent) {
53         this.parentFuture = parent;
54     }
55
56     /**
57      *
58      * {@inheritDoc}
59      *
60      * @implSpec
61      *           This implementation returns a new HeritableFuture instance that uses
62      *           the current instance as a parent. Cancellation of the child will result in
63      *           cancellation of the parent.
64      */
65     @Override
66     public <U> CompletableFuture<U> newIncompleteFuture() {
67         return new HeritableFuture<>(this);
68     }
69
70     protected void setParentFuture(Supplier<@Nullable Future<?>> futureSupplier) {
71         synchronized (futureLock) {
72             var future = futureSupplier.get();
73             if (future != this) {
74                 if (isCancelled() && future != null) {
75                     future.cancel(true);
76                 } else {
77                     parentFuture = future;
78                 }
79             }
80         }
81     }
82
83     /**
84      *
85      * {@inheritDoc}
86      *
87      * @implSpec
88      *           This implementation cancels this future first, then cancels the parent future.
89      */
90     @Override
91     public boolean cancel(boolean mayInterruptIfRunning) {
92         if (completeExceptionally(new CancellationException())) {
93             synchronized (futureLock) {
94                 var future = parentFuture;
95                 parentFuture = null;
96                 if (future != null) {
97                     future.cancel(mayInterruptIfRunning);
98                 }
99             }
100             return true;
101         }
102         return isCancelled();
103     }
104
105     /**
106      *
107      * {@inheritDoc}
108      *
109      * @implSpec
110      *           This implementation will treat the future returned by the function as a parent future.
111      */
112     @Override
113     @NonNullByDefault({}) // the generics here don't play well with the null checker
114     public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {
115         return new ComposeFunctionWrapper<>(fn, false, null).returnedFuture;
116     }
117
118     /**
119      *
120      * {@inheritDoc}
121      *
122      * @implSpec
123      *           This implementation will treat the future returned by the function as a parent future.
124      */
125     @Override
126     @NonNullByDefault({}) // the generics here don't play well with the null checker
127     public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) {
128         return new ComposeFunctionWrapper<>(fn, true, null).returnedFuture;
129     }
130
131     /**
132      *
133      * {@inheritDoc}
134      *
135      * @implSpec
136      *           This implementation will treat the future returned by the function as a parent future.
137      */
138     @Override
139     @NonNullByDefault({}) // the generics here don't play well with the null checker
140     public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,
141             Executor executor) {
142         return new ComposeFunctionWrapper<>(fn, true, executor).returnedFuture;
143     }
144
145     /**
146      * This class is responsible for wrapping the supplied compose function.
147      * The instant the function returns the next CompletionStage, the parentFuture of the downstream HeritableFuture
148      * will be reassigned to the completion stage. This way cancellations of
149      * downstream futures will be able to reach the future returned by the supplied function.
150      *
151      * Most of the complexity going on in this class is due to the fact that the apply function might be
152      * called while calling `super.thenCompose`. This would happen if the current future is already complete
153      * since the next stage would be started immediately either on the current thread or asynchronously.
154      *
155      * @param <U> the type to be returned by the composed future
156      */
157     private class ComposeFunctionWrapper<U> implements Function<T, CompletionStage<U>> {
158
159         private final Object fieldsLock = new Object();
160         private final Function<? super T, ? extends CompletionStage<U>> fn;
161         private @Nullable HeritableFuture<U> composedFuture;
162         private @Nullable CompletionStage<U> innerStage;
163         // The final composed future to be used by users of this wrapper class
164         final HeritableFuture<U> returnedFuture;
165
166         public ComposeFunctionWrapper(Function<? super T, ? extends CompletionStage<U>> fn, boolean async,
167                 @Nullable Executor executor) {
168             this.fn = fn;
169
170             var f = (HeritableFuture<U>) thenCompose(async, executor);
171             synchronized (fieldsLock) {
172                 this.composedFuture = f;
173                 var stage = innerStage;
174                 if (stage != null) {
175                     // getting here means that the `apply` function was run before `composedFuture` was initialized.
176                     f.setParentFuture(stage::toCompletableFuture);
177                 }
178             }
179             this.returnedFuture = f;
180         }
181
182         private CompletableFuture<U> thenCompose(boolean async, @Nullable Executor executor) {
183             if (!async) {
184                 return HeritableFuture.super.thenCompose(this);
185             }
186             if (executor == null) {
187                 return HeritableFuture.super.thenComposeAsync(this);
188             }
189             return HeritableFuture.super.thenComposeAsync(this, executor);
190         }
191
192         @Override
193         public CompletionStage<U> apply(T t) {
194             CompletionStage<U> stage = fn.apply(t);
195             synchronized (fieldsLock) {
196                 var f = composedFuture;
197                 if (f == null) {
198                     // We got here before the wrapper finished initializing, so that
199                     // means that the enclosing future was already complete at the time `super.thenCompose` was called.
200                     // In which case the best we can do is save this stage so that the constructor can finish the job.
201                     innerStage = stage;
202                 } else {
203                     f.setParentFuture(stage::toCompletableFuture);
204                 }
205             }
206             return stage;
207         }
208     }
209 }