blob: 3740b5f6ce03a755efc192d61addad865d77b7ee [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.lib.skyframe.SkylarkImportLookupValue.SkylarkImportLookupKey;
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;
class CachedSkylarkImportLookupValueAndDeps {
private final SkylarkImportLookupKey key;
private final SkylarkImportLookupValue value;
private final ImmutableList<Iterable<SkyKey>> directDeps;
private final ImmutableList<CachedSkylarkImportLookupValueAndDeps> transitiveDeps;
private CachedSkylarkImportLookupValueAndDeps(
SkylarkImportLookupKey key,
SkylarkImportLookupValue value,
ImmutableList<Iterable<SkyKey>> directDeps,
ImmutableList<CachedSkylarkImportLookupValueAndDeps> transitiveDeps) {
this.key = key;
this.value = value;
this.directDeps = directDeps;
this.transitiveDeps = transitiveDeps;
}
void traverse(
DepGroupConsumer depGroupConsumer,
Map<SkylarkImportLookupKey, CachedSkylarkImportLookupValueAndDeps> visitedDeps)
throws InterruptedException {
for (Iterable<SkyKey> directDepGroup : directDeps) {
depGroupConsumer.accept(directDepGroup);
}
for (CachedSkylarkImportLookupValueAndDeps indirectDeps : transitiveDeps) {
if (!visitedDeps.containsKey(indirectDeps.key)) {
visitedDeps.put(indirectDeps.key, indirectDeps);
indirectDeps.traverse(depGroupConsumer, visitedDeps);
}
}
}
SkylarkImportLookupValue getValue() {
return value;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof CachedSkylarkImportLookupValueAndDeps) {
// With the interner, force there to be exactly one cached value per key at any given point
// in time.
return this.key.equals(((CachedSkylarkImportLookupValueAndDeps) 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<CachedSkylarkImportLookupValueAndDeps> interner) {
this.interner = interner;
}
private final Interner<CachedSkylarkImportLookupValueAndDeps> interner;
private final List<Iterable<SkyKey>> directDeps = new ArrayList<>();
private final List<CachedSkylarkImportLookupValueAndDeps> transitiveDeps = new ArrayList<>();
private final AtomicReference<Exception> exceptionSeen = new AtomicReference<>(null);
private SkylarkImportLookupValue value;
private SkylarkImportLookupKey key;
@CanIgnoreReturnValue
Builder addDep(SkyKey key) {
Preconditions.checkState(
transitiveDeps.isEmpty(), "Expected transitive deps to be loaded last: %s", this);
directDeps.add(ImmutableList.of(key));
return this;
}
@CanIgnoreReturnValue
Builder addDeps(Iterable<SkyKey> keys) {
Preconditions.checkState(
transitiveDeps.isEmpty(), "Expected transitive deps to be loaded last: %s", this);
directDeps.add(keys);
return this;
}
@CanIgnoreReturnValue
Builder noteException(Exception e) {
exceptionSeen.set(e);
return this;
}
@CanIgnoreReturnValue
Builder addTransitiveDeps(CachedSkylarkImportLookupValueAndDeps transitiveDeps) {
this.transitiveDeps.add(transitiveDeps);
return this;
}
@CanIgnoreReturnValue
Builder setValue(SkylarkImportLookupValue value) {
this.value = value;
return this;
}
@CanIgnoreReturnValue
Builder setKey(SkylarkImportLookupKey key) {
this.key = key;
return this;
}
CachedSkylarkImportLookupValueAndDeps build() {
// We expect that we don't handle any exceptions in SkylarkLookupImportFunction 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 CachedSkylarkImportLookupValueAndDeps(
key, value, ImmutableList.copyOf(directDeps), ImmutableList.copyOf(transitiveDeps)));
}
@Override
public String toString() {
return MoreObjects.toStringHelper(CachedSkylarkImportLookupValueAndDeps.Builder.class)
.add("key", key)
.add("value", value)
.add("directDeps", directDeps)
.add("transitiveDeps", transitiveDeps)
.add("exceptionSeen", exceptionSeen)
.toString();
}
}
}