| // Copyright 2018 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.Preconditions; |
| |
| /** |
| * For use when the {@link #argument} of the {@link SkyKey} cannot be a {@link SkyKey} itself, |
| * either because it is a type like List or because it is already a different {@link SkyKey}. |
| * Provides convenient boilerplate. |
| */ |
| public abstract class AbstractSkyKey<T> implements SkyKey { |
| // Visible for serialization. |
| protected final T arg; |
| /** |
| * Cache the hash code for this object. It might be expensive to compute. It is transient because |
| * argument's hash code might not be stable across JVM instances. |
| */ |
| private transient int hashCode; |
| |
| protected AbstractSkyKey(T arg) { |
| this.arg = Preconditions.checkNotNull(arg); |
| } |
| |
| @Override |
| public final int hashCode() { |
| // We use the hash code caching strategy employed by java.lang.String. There are three subtle |
| // things going on here: |
| // |
| // (1) We use a value of 0 to indicate that the hash code hasn't been computed and cached yet. |
| // Yes, this means that if the hash code is really 0 then we will "recompute" it each time. But |
| // this isn't a problem in practice since a hash code of 0 should be rare. |
| // |
| // (2) Since we have no synchronization, multiple threads can race here thinking there are the |
| // first one to compute and cache the hash code. |
| // |
| // (3) Moreover, since 'hashCode' is non-volatile, the cached hash code value written from one |
| // thread may not be visible by another. |
| // |
| // All three of these issues are benign from a correctness perspective; in the end we have no |
| // overhead from synchronization, at the cost of potentially computing the hash code more than |
| // once. |
| int h = hashCode; |
| if (h == 0) { |
| h = computeHashCode(); |
| hashCode = h; |
| } |
| return h; |
| } |
| |
| @Override |
| public final T argument() { |
| return arg; |
| } |
| |
| private int computeHashCode() { |
| return 31 * functionName().hashCode() + arg.hashCode(); |
| } |
| |
| @Override |
| public final boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null || getClass() != obj.getClass()) { |
| return false; |
| } |
| AbstractSkyKey<?> that = (AbstractSkyKey<?>) obj; |
| return this.functionName().equals(that.functionName()) && this.arg.equals(that.arg); |
| } |
| |
| @Override |
| public String toString() { |
| return functionName() + ":" + arg; |
| } |
| } |