2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.bluetooth.util;
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;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
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}.
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.
40 * @author Connor Petty - Initial contribution
44 public class HeritableFuture<T> extends CompletableFuture<T> {
46 protected final Object futureLock = new Object();
47 protected @Nullable Future<?> parentFuture;
49 public HeritableFuture() {
52 public HeritableFuture(Future<?> parent) {
53 this.parentFuture = parent;
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.
66 public <U> CompletableFuture<U> newIncompleteFuture() {
67 return new HeritableFuture<>(this);
70 protected void setParentFuture(Supplier<@Nullable Future<?>> futureSupplier) {
71 synchronized (futureLock) {
72 var future = futureSupplier.get();
74 if (isCancelled() && future != null) {
77 parentFuture = future;
88 * This implementation cancels this future first, then cancels the parent future.
91 public boolean cancel(boolean mayInterruptIfRunning) {
92 if (completeExceptionally(new CancellationException())) {
93 synchronized (futureLock) {
94 var future = parentFuture;
97 future.cancel(mayInterruptIfRunning);
102 return isCancelled();
110 * This implementation will treat the future returned by the function as a parent future.
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;
123 * This implementation will treat the future returned by the function as a parent future.
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;
136 * This implementation will treat the future returned by the function as a parent future.
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,
142 return new ComposeFunctionWrapper<>(fn, true, executor).returnedFuture;
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.
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.
155 * @param <U> the type to be returned by the composed future
157 private class ComposeFunctionWrapper<U> implements Function<T, CompletionStage<U>> {
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;
166 public ComposeFunctionWrapper(Function<? super T, ? extends CompletionStage<U>> fn, boolean async,
167 @Nullable Executor executor) {
170 var f = (HeritableFuture<U>) thenCompose(async, executor);
171 synchronized (fieldsLock) {
172 this.composedFuture = f;
173 var stage = innerStage;
175 // getting here means that the `apply` function was run before `composedFuture` was initialized.
176 f.setParentFuture(stage::toCompletableFuture);
179 this.returnedFuture = f;
182 private CompletableFuture<U> thenCompose(boolean async, @Nullable Executor executor) {
184 return HeritableFuture.super.thenCompose(this);
186 if (executor == null) {
187 return HeritableFuture.super.thenComposeAsync(this);
189 return HeritableFuture.super.thenComposeAsync(this, executor);
193 public CompletionStage<U> apply(T t) {
194 CompletionStage<U> stage = fn.apply(t);
195 synchronized (fieldsLock) {
196 var f = composedFuture;
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.
203 f.setParentFuture(stage::toCompletableFuture);