blob: d8970c9b0bf011962622e39a17da5f90e85d765e [file] [log] [blame]
// Copyright 2017 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 com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.Interner;
import com.google.devtools.build.lib.concurrent.BlazeInterners;
/**
* Container for a pending operation on the reverse deps set. We use subclasses to save 8 bytes of
* memory instead of keeping a field in this class, and we store {@link Op#CHECK} or {@link Op#ADD}
* operations as the bare {@link SkyKey} in order to save the wrapper object in that case.
*
* <p>When a list of {@link KeyToConsolidate} operations is processed, each operation is performed
* in order. Operations on a done or freshly evaluating node entry are straightforward: they apply
* to the entry's reverse deps. Operations on a re-evaluating node entry have a double meaning: they
* will eventually be applied to the node entry's existing reverse deps, just as for a done node
* entry, but they are also used to track the entries that declared/redeclared a reverse dep on this
* entry during this evaluation (and will thus need to be signaled when this entry finishes
* evaluating).
*/
public abstract class KeyToConsolidate {
enum Op {
/**
* Assert that the reverse dep is already present in the set of reverse deps. If the entry is
* re-evaluating, add this reverse dep to the set of reverse deps to signal when this entry is
* done.
*/
CHECK,
/**
* Add the reverse dep to the set of reverse deps and assert it was not already present. If the
* entry is re-evaluating, add this reverse dep to the set of reverse deps to signal when this
* entry is done.
*/
ADD,
/**
* Remove the reverse dep from the set of reverse deps and assert it was present. If the entry
* is re-evaluating, also remove the reverse dep from the set of reverse deps to signal when
* this entry is done, and assert that it was present.
*/
REMOVE,
/**
* The same as {@link #REMOVE}, except that if the entry is re-evaluating, we assert that the
* set of reverse deps to signal did <i>not</i> contain this reverse dep.
*/
REMOVE_OLD
}
/** The operation {@link ReverseDepsUtility} should store bare in pending reverse dep ops. */
public enum OpToStoreBare {
ADD(Op.ADD),
CHECK(Op.CHECK);
private final Op op;
OpToStoreBare(Op op) {
this.op = op;
}
}
private static final Interner<KeyToConsolidate> consolidateInterner =
BlazeInterners.newWeakInterner();
private final SkyKey key;
/** Do not call directly -- use the {@link #create} static method instead. */
private KeyToConsolidate(SkyKey key) {
this.key = key;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("key", key).toString();
}
/**
* Gets which operation was delayed for the given object, created using {@link #create}. The same
* {@code opToStoreBare} passed in to {@link #create} should be passed in here.
*/
static Op op(Object obj, OpToStoreBare opToStoreBare) {
if (obj instanceof SkyKey) {
return opToStoreBare.op;
}
if (obj instanceof KeyToAdd) {
return Op.ADD;
}
if (obj instanceof KeyToCheck) {
return Op.CHECK;
}
if (obj instanceof KeyToRemove) {
return Op.REMOVE;
}
if (obj instanceof KeyToRemoveOld) {
return Op.REMOVE_OLD;
}
throw new IllegalStateException(
"Unknown object type: " + obj + ", " + opToStoreBare + ", " + obj.getClass());
}
/** Gets the key whose operation was delayed for the given object. */
static SkyKey key(Object obj) {
if (obj instanceof SkyKey) {
return (SkyKey) obj;
}
Preconditions.checkState(obj instanceof KeyToConsolidate, obj);
return ((KeyToConsolidate) obj).key;
}
/**
* Creates a new operation, encoding the operation {@code op} with reverse dep {@code key}. To
* save memory, the caller should specify the most common operation expected as {@code
* opToStoreBare}. That operation will be encoded as the raw {@code key}, saving the memory of an
* object wrapper. Whatever {@code opToStoreBare} is set to here, the same value must be passed in
* to {@link #op} when decoding an operation emitted by this method.
*/
static Object create(SkyKey key, Op op, OpToStoreBare opToStoreBare) {
if (op == opToStoreBare.op) {
return key;
}
switch (op) {
case CHECK:
return consolidateInterner.intern(new KeyToCheck(key));
case REMOVE:
return consolidateInterner.intern(new KeyToRemove(key));
case REMOVE_OLD:
return consolidateInterner.intern(new KeyToRemoveOld(key));
case ADD:
return consolidateInterner.intern(new KeyToAdd(key));
default:
throw new IllegalStateException(op + ", " + key);
}
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
return this.getClass() == obj.getClass() && this.key.equals(((KeyToConsolidate) obj).key);
}
protected int keyHashCode() {
return key.hashCode();
}
@Override
public int hashCode() {
// Overridden in subclasses.
throw new UnsupportedOperationException(key.toString());
}
private static final class KeyToAdd extends KeyToConsolidate {
KeyToAdd(SkyKey key) {
super(key);
}
@Override
public int hashCode() {
return keyHashCode();
}
}
private static final class KeyToCheck extends KeyToConsolidate {
KeyToCheck(SkyKey key) {
super(key);
}
@Override
public int hashCode() {
return 31 + 43 * keyHashCode();
}
}
private static final class KeyToRemove extends KeyToConsolidate {
KeyToRemove(SkyKey key) {
super(key);
}
@Override
public int hashCode() {
return 42 + 37 * keyHashCode();
}
}
private static final class KeyToRemoveOld extends KeyToConsolidate {
KeyToRemoveOld(SkyKey key) {
super(key);
}
@Override
public int hashCode() {
return 93 + 37 * keyHashCode();
}
}
}