blob: 77682f3e7c76650b54b61ef79d9eb5771cd76cbe [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.state;
import static com.google.common.base.MoreObjects.toStringHelper;
import com.google.devtools.build.skyframe.SkyFunction.LookupEnvironment;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import com.google.devtools.build.skyframe.SkyframeLookupResult;
import com.google.errorprone.annotations.ForOverride;
import java.util.function.Consumer;
/** Captures information about a lookup requested by a state machine. */
abstract class Lookup implements SkyframeLookupResult.QueryDepCallback {
private final TaskTreeNode parent;
final SkyKey key;
private Lookup(TaskTreeNode parent, SkyKey key) {
this.parent = parent;
this.key = key;
}
final SkyKey key() {
return key;
}
/**
* Performs a lookup directly against the environment.
*
* <p>This is more efficient than {@link LookupEnvironment#getValuesAndExceptions} when there is
* only one key at a time.
*
* @return true if a value was available or an exception was handled. Note: this is false for
* unhandled exceptions.
*/
abstract boolean doLookup(LookupEnvironment env) throws InterruptedException;
@Override
public final void acceptValue(SkyKey unusedKey, SkyValue value) {
acceptValueInternal(value);
parent.signalChildDoneAndEnqueueIfReady();
}
@ForOverride
protected abstract void acceptValueInternal(SkyValue value);
@Override
public final boolean tryHandleException(SkyKey unusedKey, Exception exception) {
boolean handled = tryHandleExceptionInternal(exception);
if (handled) {
parent.signalChildDoneAndEnqueueIfReady();
}
return handled;
}
@ForOverride
protected abstract boolean tryHandleExceptionInternal(Exception exception);
static final class ConsumerLookup extends Lookup {
private final Consumer<SkyValue> sink;
ConsumerLookup(TaskTreeNode parent, SkyKey key, Consumer<SkyValue> sink) {
super(parent, key);
this.sink = sink;
}
@Override
boolean doLookup(LookupEnvironment env) throws InterruptedException {
var value = env.getValue(key);
if (value == null) {
return false;
}
acceptValue(key, value);
return true;
}
@Override
protected void acceptValueInternal(SkyValue value) {
sink.accept(value);
}
@Override
protected boolean tryHandleExceptionInternal(Exception unusedException) {
return false;
}
}
static final class ValueOrExceptionLookup<E extends Exception> extends Lookup {
private final Class<E> exceptionClass;
private final StateMachine.ValueOrExceptionSink<E> sink;
ValueOrExceptionLookup(
TaskTreeNode parent,
SkyKey key,
Class<E> exceptionClass,
StateMachine.ValueOrExceptionSink<E> sink) {
super(parent, key);
this.exceptionClass = exceptionClass;
this.sink = sink;
}
@Override
boolean doLookup(LookupEnvironment env) throws InterruptedException {
SkyValue value;
try {
if ((value = env.getValueOrThrow(key(), exceptionClass)) == null) {
return false;
}
acceptValue(key, value);
} catch (Exception e) {
if (e instanceof InterruptedException) {
throw (InterruptedException) e;
}
if (!tryHandleException(key, e)) {
throw new IllegalArgumentException("Unexpected exception for " + key(), e);
}
}
return true;
}
@Override
protected void acceptValueInternal(SkyValue value) {
sink.acceptValueOrException(value, /* exception= */ null);
}
@Override
protected boolean tryHandleExceptionInternal(Exception exception) {
if (exceptionClass.isInstance(exception)) {
sink.acceptValueOrException(/* value= */ null, exceptionClass.cast(exception));
return true;
}
return false;
}
}
static final class ValueOrException2Lookup<E1 extends Exception, E2 extends Exception>
extends Lookup {
private final Class<E1> exceptionClass1;
private final Class<E2> exceptionClass2;
private final StateMachine.ValueOrException2Sink<E1, E2> sink;
ValueOrException2Lookup(
TaskTreeNode parent,
SkyKey key,
Class<E1> exceptionClass1,
Class<E2> exceptionClass2,
StateMachine.ValueOrException2Sink<E1, E2> sink) {
super(parent, key);
this.exceptionClass1 = exceptionClass1;
this.exceptionClass2 = exceptionClass2;
this.sink = sink;
}
@Override
boolean doLookup(LookupEnvironment env) throws InterruptedException {
SkyValue value;
try {
if ((value = env.getValueOrThrow(key(), exceptionClass1, exceptionClass2)) == null) {
return false;
}
acceptValue(key, value);
} catch (Exception e) {
if (e instanceof InterruptedException) {
throw (InterruptedException) e;
}
if (!tryHandleException(key, e)) {
throw new IllegalArgumentException("Unexpected exception for " + key(), e);
}
}
return true;
}
@Override
protected void acceptValueInternal(SkyValue value) {
sink.acceptValueOrException2(value, /* e1= */ null, /* e2= */ null);
}
@Override
protected boolean tryHandleExceptionInternal(Exception exception) {
if (exceptionClass1.isInstance(exception)) {
sink.acceptValueOrException2(
/* value= */ null, exceptionClass1.cast(exception), /* e2= */ null);
return true;
}
if (exceptionClass2.isInstance(exception)) {
sink.acceptValueOrException2(
/* value= */ null, /* e1= */ null, exceptionClass2.cast(exception));
return true;
}
return false;
}
}
static final class ValueOrException3Lookup<
E1 extends Exception, E2 extends Exception, E3 extends Exception>
extends Lookup {
private final Class<E1> exceptionClass1;
private final Class<E2> exceptionClass2;
private final Class<E3> exceptionClass3;
private final StateMachine.ValueOrException3Sink<E1, E2, E3> sink;
ValueOrException3Lookup(
TaskTreeNode parent,
SkyKey key,
Class<E1> exceptionClass1,
Class<E2> exceptionClass2,
Class<E3> exceptionClass3,
StateMachine.ValueOrException3Sink<E1, E2, E3> sink) {
super(parent, key);
this.exceptionClass1 = exceptionClass1;
this.exceptionClass2 = exceptionClass2;
this.exceptionClass3 = exceptionClass3;
this.sink = sink;
}
@Override
boolean doLookup(LookupEnvironment env) throws InterruptedException {
SkyValue value;
try {
if ((value = env.getValueOrThrow(key(), exceptionClass1, exceptionClass2, exceptionClass3))
== null) {
return false;
}
acceptValue(key, value);
} catch (Exception e) {
if (e instanceof InterruptedException) {
throw (InterruptedException) e;
}
if (!tryHandleException(key, e)) {
throw new IllegalArgumentException("Unexpected exception for " + key(), e);
}
}
return true;
}
@Override
protected void acceptValueInternal(SkyValue value) {
sink.acceptValueOrException3(value, /* e1= */ null, /* e2= */ null, /* e3= */ null);
}
@Override
protected boolean tryHandleExceptionInternal(Exception exception) {
if (exceptionClass1.isInstance(exception)) {
sink.acceptValueOrException3(
/* value= */ null, exceptionClass1.cast(exception), /* e2= */ null, /* e3= */ null);
return true;
}
if (exceptionClass2.isInstance(exception)) {
sink.acceptValueOrException3(
/* value= */ null, /* e1= */ null, exceptionClass2.cast(exception), /* e3= */ null);
return true;
}
if (exceptionClass3.isInstance(exception)) {
sink.acceptValueOrException3(
/* value= */ null, /* e1= */ null, /* e2= */ null, exceptionClass3.cast(exception));
return true;
}
return false;
}
}
@Override
public String toString() {
return toStringHelper(this).add("parent", parent).add("key", key).toString();
}
}