blob: df47c3384b583c75e4b4f3d77b7f9a5ae6960c20 [file] [log] [blame]
// Copyright 2019 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;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Interner;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
/**
* A saved {@link BzlLoadFunction} computation, used when inlining that Skyfunction.
*
* <p>This holds a requested key, its computed value, and the direct and transitive Skyframe
* dependencies that are needed to compute it from scratch (i.e. if it weren't cached). Here
* "transitive" means "underneath another {@code BzlLoadFunction} computation"; we split them into
* other {@code CachedBzlLoadData} objects so they can be shared by other requesting bzls.
*/
class CachedBzlLoadData {
private final BzlLoadValue.Key key;
private final BzlLoadValue value;
private final ImmutableList<Iterable<SkyKey>> directDeps;
private final ImmutableList<CachedBzlLoadData> transitiveDeps;
private CachedBzlLoadData(
BzlLoadValue.Key key,
BzlLoadValue value,
ImmutableList<Iterable<SkyKey>> directDeps,
ImmutableList<CachedBzlLoadData> transitiveDeps) {
this.key = key;
this.value = value;
this.directDeps = directDeps;
this.transitiveDeps = transitiveDeps;
}
/**
* Adds all deps (direct and transitive) of this value to the {@code visitedDeps} set and passes
* them to the consumer (with unspecified order and grouping). The traversal does not include
* nodes already contained in {@code visitedDeps}.
*/
void traverse(
DepGroupConsumer depGroupConsumer, Map<BzlLoadValue.Key, CachedBzlLoadData> visitedDeps)
throws InterruptedException {
if (visitedDeps.putIfAbsent(key, this) != null) {
return;
}
for (Iterable<SkyKey> directDepGroup : directDeps) {
depGroupConsumer.accept(directDepGroup);
}
for (CachedBzlLoadData indirectDeps : transitiveDeps) {
indirectDeps.traverse(depGroupConsumer, visitedDeps);
}
}
BzlLoadValue getValue() {
return value;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof CachedBzlLoadData) {
// With the interner, force there to be exactly one cached value per key at any given point
// in time.
return this.key.equals(((CachedBzlLoadData) obj).key);
}
return false;
}
@Override
public int hashCode() {
return key.hashCode();
}
/** A consumer of dependency groups that can be interrupted. */
interface DepGroupConsumer {
void accept(Iterable<SkyKey> keys) throws InterruptedException;
}
static class Builder {
Builder(Interner<CachedBzlLoadData> interner) {
this.interner = interner;
}
private final Interner<CachedBzlLoadData> interner;
private final List<Iterable<SkyKey>> directDeps = new ArrayList<>();
private final List<CachedBzlLoadData> transitiveDeps = new ArrayList<>();
private final AtomicReference<Exception> exceptionSeen = new AtomicReference<>(null);
private BzlLoadValue value;
private BzlLoadValue.Key key;
@CanIgnoreReturnValue
Builder addDep(SkyKey key) {
directDeps.add(ImmutableList.of(key));
return this;
}
@CanIgnoreReturnValue
Builder addDeps(Iterable<SkyKey> keys) {
directDeps.add(keys);
return this;
}
@CanIgnoreReturnValue
Builder noteException(Exception e) {
exceptionSeen.set(e);
return this;
}
@CanIgnoreReturnValue
Builder addTransitiveDeps(CachedBzlLoadData transitiveDeps) {
this.transitiveDeps.add(transitiveDeps);
return this;
}
@CanIgnoreReturnValue
Builder setValue(BzlLoadValue value) {
this.value = value;
return this;
}
@CanIgnoreReturnValue
Builder setKey(BzlLoadValue.Key key) {
this.key = key;
return this;
}
CachedBzlLoadData build() {
// We expect that we don't handle any exceptions in BzlLoadFunction directly.
Preconditions.checkState(exceptionSeen.get() == null, "Caching a value in error?: %s", this);
Preconditions.checkNotNull(value, "Expected value to be set: %s", this);
Preconditions.checkNotNull(key, "Expected key to be set: %s", this);
return interner.intern(
new CachedBzlLoadData(
key, value, ImmutableList.copyOf(directDeps), ImmutableList.copyOf(transitiveDeps)));
}
@Override
public String toString() {
return MoreObjects.toStringHelper(CachedBzlLoadData.Builder.class)
.add("key", key)
.add("value", value)
.add("directDeps", directDeps)
.add("transitiveDeps", transitiveDeps)
.add("exceptionSeen", exceptionSeen)
.toString();
}
}
}