blob: 9f79541ffdc9eaf86a246efb89ff009508a7a6a2 [file] [log] [blame]
// Copyright 2022 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.skyframe;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Iterator;
import java.util.NoSuchElementException;
import javax.annotation.Nullable;
/**
* An iterable-like result of getting Skyframe dependencies via {@link
* SkyFunction.Environment#getOrderedValuesAndExceptions}. Callers can use the {@link #next} and
* {@link #nextOrThrow} methods to obtain successive elements.
*
* <p>Can only be iterated through once: if an iterable of size k was passed to {@link
* SkyFunction.Environment#getOrderedValuesAndExceptions}, then the returned {@link
* SkyframeIterableResult} can have {@link #next} and {@link #nextOrThrow} called a total of k
* times.
*
* <p>Note that a {@link SkyFunction} cannot guarantee that {@link
* SkyFunction.Environment#valuesMissing} will be true upon receipt of a {@code
* SkyframeIterableResult}. The elements must be iterated over. If {@link #next} or {@link
* #nextOrThrow} returns {@code null}, only then will {@link SkyFunction.Environment#valuesMissing}
* be guaranteed to return true.
*/
public final class SkyframeIterableResult {
private final Iterator<ValueOrUntypedException> valuesOrExceptions;
private final Runnable valuesMissingCallback;
SkyframeIterableResult(
Runnable valuesMissingCallback, Iterator<ValueOrUntypedException> valuesOrExceptions) {
this.valuesMissingCallback = checkNotNull(valuesMissingCallback);
this.valuesOrExceptions = checkNotNull(valuesOrExceptions);
}
/**
* Returns true if {@link SkyframeIterableResult} has more elements. In other words, returns true
* if {@link #next} or {@link #nextOrThrow} would return an element rather than throwing a {@link
* NoSuchElementException}.
*/
public boolean hasNext() {
return valuesOrExceptions.hasNext();
}
/**
* Returns the next direct dependency in {@link SkyframeIterableResult}. If the dependency is not
* in the set of already evaluated direct dependencies, returns {@code null}. Also returns {@code
* null} if the dependency has already been evaluated and found to be in error. In either of these
* cases, {@link SkyFunction.Environment#valuesMissing} will subsequently return true.
*/
public SkyValue next() {
return nextOrThrow(null, null, null);
}
/**
* Returns the next direct dependency in {@link SkyframeIterableResult}. If the dependency is not
* in the set of already evaluated direct dependencies, returns {@code null}. If the dependency
* has already been evaluated and found to be in error, throws the exception coming from the
* error, so long as the exception is of one of the specified types. SkyFunction implementations
* may use this method to catch and rethrow a more informative exception or to continue
* evaluation. The caller must specify the exception type(s) that might be thrown using the {@code
* exceptionClass} argument(s). If the dependency's exception is not an instance of {@code
* exceptionClass}, {@code null} is returned. In this case, {@link
* SkyFunction.Environment#valuesMissing} will subsequently return true.
*
* <p>The exception class given cannot be a supertype or a subtype of {@link RuntimeException}, or
* a subtype of {@link InterruptedException}. See {@link
* SkyFunctionException#validateExceptionType} for details.
*/
public <E1 extends Exception> SkyValue nextOrThrow(Class<E1> exceptionClass) throws E1 {
return nextOrThrow(exceptionClass, null, null, null);
}
public <E1 extends Exception, E2 extends Exception> SkyValue nextOrThrow(
Class<E1> exceptionClass1, Class<E2> exceptionClass2) throws E1, E2 {
return nextOrThrow(exceptionClass1, exceptionClass2, null, null);
}
public <E1 extends Exception, E2 extends Exception, E3 extends Exception> SkyValue nextOrThrow(
Class<E1> exceptionClass1, Class<E2> exceptionClass2, Class<E3> exceptionClass3)
throws E1, E2, E3 {
return nextOrThrow(exceptionClass1, exceptionClass2, exceptionClass3, null);
}
public <E1 extends Exception, E2 extends Exception, E3 extends Exception, E4 extends Exception>
SkyValue nextOrThrow(
@Nullable Class<E1> exceptionClass1,
@Nullable Class<E2> exceptionClass2,
@Nullable Class<E3> exceptionClass3,
@Nullable Class<E4> exceptionClass4)
throws E1, E2, E3, E4 {
ValueOrUntypedException voe = valuesOrExceptions.next();
SkyValue value = voe.getValue();
if (value != null) {
return value;
}
Exception e = voe.getException();
if (e != null) {
if (exceptionClass1 != null && exceptionClass1.isInstance(e)) {
throw exceptionClass1.cast(e);
}
if (exceptionClass2 != null && exceptionClass2.isInstance(e)) {
throw exceptionClass2.cast(e);
}
if (exceptionClass3 != null && exceptionClass3.isInstance(e)) {
throw exceptionClass3.cast(e);
}
if (exceptionClass4 != null && exceptionClass4.isInstance(e)) {
throw exceptionClass4.cast(e);
}
}
valuesMissingCallback.run();
return null;
}
}