2 * Copyright (c) 2010-2023 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 @SuppressWarnings("PMD.CompareObjectsWithEquals")
71 protected void setParentFuture(Supplier<@Nullable Future<?>> futureSupplier) {
72 synchronized (futureLock) {
73 var future = futureSupplier.get();
75 if (isCancelled() && future != null) {
78 parentFuture = future;
89 * This implementation cancels this future first, then cancels the parent future.
92 public boolean cancel(boolean mayInterruptIfRunning) {
93 if (completeExceptionally(new CancellationException())) {
94 synchronized (futureLock) {
95 var future = parentFuture;
98 future.cancel(mayInterruptIfRunning);
103 return isCancelled();
111 * This implementation will treat the future returned by the function as a parent future.
114 @NonNullByDefault({}) // the generics here don't play well with the null checker
115 public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {
116 return new ComposeFunctionWrapper<>(fn, false, null).returnedFuture;
124 * This implementation will treat the future returned by the function as a parent future.
127 @NonNullByDefault({}) // the generics here don't play well with the null checker
128 public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) {
129 return new ComposeFunctionWrapper<>(fn, true, null).returnedFuture;
137 * This implementation will treat the future returned by the function as a parent future.
140 @NonNullByDefault({}) // the generics here don't play well with the null checker
141 public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,
143 return new ComposeFunctionWrapper<>(fn, true, executor).returnedFuture;
147 * This class is responsible for wrapping the supplied compose function.
148 * The instant the function returns the next CompletionStage, the parentFuture of the downstream HeritableFuture
149 * will be reassigned to the completion stage. This way cancellations of
150 * downstream futures will be able to reach the future returned by the supplied function.
152 * Most of the complexity going on in this class is due to the fact that the apply function might be
153 * called while calling `super.thenCompose`. This would happen if the current future is already complete
154 * since the next stage would be started immediately either on the current thread or asynchronously.
156 * @param <U> the type to be returned by the composed future
158 private class ComposeFunctionWrapper<U> implements Function<T, CompletionStage<U>> {
160 private final Object fieldsLock = new Object();
161 private final Function<? super T, ? extends CompletionStage<U>> fn;
162 private @Nullable HeritableFuture<U> composedFuture;
163 private @Nullable CompletionStage<U> innerStage;
164 // The final composed future to be used by users of this wrapper class
165 final HeritableFuture<U> returnedFuture;
167 public ComposeFunctionWrapper(Function<? super T, ? extends CompletionStage<U>> fn, boolean async,
168 @Nullable Executor executor) {
171 var f = (HeritableFuture<U>) thenCompose(async, executor);
172 synchronized (fieldsLock) {
173 this.composedFuture = f;
174 var stage = innerStage;
176 // getting here means that the `apply` function was run before `composedFuture` was initialized.
177 f.setParentFuture(stage::toCompletableFuture);
180 this.returnedFuture = f;
183 private CompletableFuture<U> thenCompose(boolean async, @Nullable Executor executor) {
185 return HeritableFuture.super.thenCompose(this);
187 if (executor == null) {
188 return HeritableFuture.super.thenComposeAsync(this);
190 return HeritableFuture.super.thenComposeAsync(this, executor);
194 public CompletionStage<U> apply(T t) {
195 CompletionStage<U> stage = fn.apply(t);
196 synchronized (fieldsLock) {
197 var f = composedFuture;
199 // We got here before the wrapper finished initializing, so that
200 // means that the enclosing future was already complete at the time `super.thenCompose` was called.
201 // In which case the best we can do is save this stage so that the constructor can finish the job.
204 f.setParentFuture(stage::toCompletableFuture);