blob: e7c435c57e8b6aeaa46622e9a3e186b461b0f84e [file] [log] [blame]
// Copyright 2024 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.skyframe.serialization;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.devtools.build.lib.bugreport.BugReporter;
import com.google.devtools.build.lib.skyframe.serialization.FingerprintValueStore.MissingFingerprintValueException;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
/** Helpers for serialization futures. */
@VisibleForTesting // package-private
public final class FutureHelpers {
/**
* Waits for {@code future} and returns the result.
*
* <p>Handles exceptions by converting them into {@link SerializationException}.
*/
@VisibleForTesting // package-private
@CanIgnoreReturnValue // may be called for side effects
public static <T> T waitForSerializationFuture(ListenableFuture<T> future)
throws SerializationException {
try {
// TODO: b/297857068 - revisit whether this should handle to interrupts. As of 02/09/24,
// serialization doesn't handle interrupts so introducing them here could lead to unforseen
// problems.
return getUninterruptibly(future);
} catch (ExecutionException e) {
throw asSerializationException(e.getCause());
}
}
/**
* Gets the done value of {@code future}.
*
* <p>Handles exceptions by converting them into {@link SerializationException}.
*/
static <T> T getDoneSerializationFuture(ListenableFuture<T> future)
throws SerializationException {
try {
return Futures.getDone(future);
} catch (ExecutionException e) {
throw asSerializationException(e.getCause());
}
}
/**
* Waits for {@code future} and returns the result.
*
* <p>Handles exceptions by converting them into {@link SerializationException}. The {@link
* SerializationException} may have a {@link MissingFingerprintValueException} cause.
*
* <p>The {@link MissingFingerprintValueException} needs special case handling anywhere this
* method is used. Outside of test code, this is only possible for via {@link
* SharedValueDeserializationContext#deserializeWithSharedValues}, which explicitly handles it.
*/
@CanIgnoreReturnValue // may be called for side effects
static <T> T waitForDeserializationFuture(ListenableFuture<T> future)
throws SerializationException {
try {
// TODO: b/297857068 - revisit whether this should handle to interrupts. As of 02/09/24,
// serialization doesn't handle interrupts so introducing them here could lead to unforseen
// problems.
return getUninterruptibly(future);
} catch (ExecutionException e) {
throw asDeserializationException(e.getCause());
}
}
/**
* Gets the done value of {@code future}.
*
* <p>May throw an {@link IllegalStateException} if the future is not done. See {@link
* #waitForDeserializationFuture} for a description the {@link SerializationException}.
*/
static <T> T getDoneDeserializationFuture(ListenableFuture<T> future)
throws SerializationException {
try {
return Futures.getDone(future);
} catch (ExecutionException e) {
throw asDeserializationException(e.getCause());
}
}
/**
* Reports any errors that occur on {@code combiner}.
*
* <p>Used when a future value is going to be discarded, but it would be inappropriate to ignore
* possible errors.
*/
static void reportAnyFailures(Futures.FutureCombiner<?> combiner) {
Futures.addCallback(
combiner.call(() -> null, directExecutor()),
new FutureCallback<Void>() {
@Override
public void onSuccess(Void unused) {}
@Override
public void onFailure(Throwable t) {
BugReporter.defaultInstance().sendBugReport(t);
}
},
directExecutor());
}
/** Combines a list of {@code Void} futures into a single future. */
static ListenableFuture<Void> aggregateStatusFutures(List<ListenableFuture<Void>> futures) {
if (futures.size() == 1) {
return futures.get(0);
}
return Futures.whenAllSucceed(futures).call(() -> null, directExecutor());
}
private static SerializationException asDeserializationException(Throwable cause) {
if (cause instanceof MissingFingerprintValueException) {
return new SerializationException(cause);
}
return asSerializationException(cause);
}
private static SerializationException asSerializationException(Throwable cause) {
if (cause instanceof SerializationException) {
return new SerializationException(cause);
}
if (cause instanceof IOException) {
return new SerializationException("serialization I/O error", cause);
}
return new SerializationException("unexpected serialization error", cause);
}
private FutureHelpers() {}
}