Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | package com.google.devtools.build.skyframe; |
| 15 | |
| 16 | import com.google.common.base.Function; |
Janak Ramakrishnan | f745e99 | 2016-03-03 08:08:50 +0000 | [diff] [blame] | 17 | import com.google.common.collect.Interner; |
Nathan Harmata | a16d9f1 | 2016-11-23 20:58:07 +0000 | [diff] [blame] | 18 | import com.google.devtools.build.lib.concurrent.BlazeInterners; |
Mark Schaller | 6df8179 | 2015-12-10 18:47:47 +0000 | [diff] [blame] | 19 | import com.google.devtools.build.lib.util.Preconditions; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 20 | |
| 21 | import java.io.Serializable; |
| 22 | |
| 23 | /** |
| 24 | * A {@link SkyKey} is effectively a pair (type, name) that identifies a Skyframe value. |
| 25 | */ |
| 26 | public final class SkyKey implements Serializable { |
Nathan Harmata | a16d9f1 | 2016-11-23 20:58:07 +0000 | [diff] [blame] | 27 | private static final Interner<SkyKey> SKY_KEY_INTERNER = BlazeInterners.newWeakInterner(); |
Janak Ramakrishnan | f745e99 | 2016-03-03 08:08:50 +0000 | [diff] [blame] | 28 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 29 | private final SkyFunctionName functionName; |
| 30 | |
| 31 | /** |
| 32 | * The name of the value. |
| 33 | * |
| 34 | * <p>This is deliberately an untyped Object so that we can use arbitrary value types (e.g., |
| 35 | * Labels, PathFragments, BuildConfigurations, etc.) as value names without incurring |
| 36 | * serialization costs in the in-memory implementation of the graph. |
| 37 | */ |
| 38 | private final Object argument; |
| 39 | |
| 40 | /** |
Mark Schaller | 906f255 | 2015-04-24 15:48:53 +0000 | [diff] [blame] | 41 | * Cache the hash code for this object. It might be expensive to compute. It is transient because |
| 42 | * argument's hash code might not be stable across JVM instances. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 43 | */ |
Mark Schaller | 906f255 | 2015-04-24 15:48:53 +0000 | [diff] [blame] | 44 | private transient int hashCode; |
| 45 | |
Janak Ramakrishnan | f745e99 | 2016-03-03 08:08:50 +0000 | [diff] [blame] | 46 | private SkyKey(SkyFunctionName functionName, Object argument) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 47 | this.functionName = Preconditions.checkNotNull(functionName); |
Janak Ramakrishnan | f745e99 | 2016-03-03 08:08:50 +0000 | [diff] [blame] | 48 | this.argument = Preconditions.checkNotNull(argument); |
Nathan Harmata | 5977735 | 2015-08-10 16:11:37 +0000 | [diff] [blame] | 49 | // 'hashCode' is non-volatile and non-final, so this write may in fact *not* be visible to other |
| 50 | // threads. But this isn't a concern from a correctness perspective. See the comments in |
| 51 | // #hashCode for more details. |
| 52 | this.hashCode = computeHashCode(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 53 | } |
| 54 | |
Janak Ramakrishnan | f745e99 | 2016-03-03 08:08:50 +0000 | [diff] [blame] | 55 | public static SkyKey create(SkyFunctionName functionName, Object argument) { |
| 56 | // Intern to save memory. |
| 57 | return SKY_KEY_INTERNER.intern(new SkyKey(functionName, argument)); |
| 58 | } |
| 59 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 60 | public SkyFunctionName functionName() { |
| 61 | return functionName; |
| 62 | } |
| 63 | |
| 64 | public Object argument() { |
| 65 | return argument; |
| 66 | } |
| 67 | |
| 68 | @Override |
| 69 | public String toString() { |
| 70 | return functionName + ":" + argument; |
| 71 | } |
| 72 | |
| 73 | @Override |
| 74 | public int hashCode() { |
Nathan Harmata | 5977735 | 2015-08-10 16:11:37 +0000 | [diff] [blame] | 75 | // We use the hash code caching strategy employed by java.lang.String. There are three subtle |
| 76 | // things going on here: |
| 77 | // |
| 78 | // (1) We use a value of 0 to indicate that the hash code hasn't been computed and cached yet. |
Nathan Harmata | 1b5c327 | 2015-08-10 16:35:51 +0000 | [diff] [blame] | 79 | // Yes, this means that if the hash code is really 0 then we will "recompute" it each time. But |
| 80 | // this isn't a problem in practice since a hash code of 0 should be rare. |
Nathan Harmata | 5977735 | 2015-08-10 16:11:37 +0000 | [diff] [blame] | 81 | // |
| 82 | // (2) Since we have no synchronization, multiple threads can race here thinking there are the |
| 83 | // first one to compute and cache the hash code. |
| 84 | // |
| 85 | // (3) Moreover, since 'hashCode' is non-volatile, the cached hash code value written from one |
| 86 | // thread may not be visible by another. |
| 87 | // |
| 88 | // All three of these issues are benign from a correctness perspective; in the end we have no |
| 89 | // overhead from synchronization, at the cost of potentially computing the hash code more than |
| 90 | // once. |
Nathan Harmata | 87992dc | 2015-08-25 18:53:55 +0000 | [diff] [blame] | 91 | int h = hashCode; |
| 92 | if (h == 0) { |
| 93 | h = computeHashCode(); |
| 94 | hashCode = h; |
Mark Schaller | 906f255 | 2015-04-24 15:48:53 +0000 | [diff] [blame] | 95 | } |
Nathan Harmata | 87992dc | 2015-08-25 18:53:55 +0000 | [diff] [blame] | 96 | return h; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 97 | } |
| 98 | |
Nathan Harmata | 5977735 | 2015-08-10 16:11:37 +0000 | [diff] [blame] | 99 | private int computeHashCode() { |
| 100 | return 31 * functionName.hashCode() + argument.hashCode(); |
Mark Schaller | 906f255 | 2015-04-24 15:48:53 +0000 | [diff] [blame] | 101 | } |
| 102 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 103 | @Override |
| 104 | public boolean equals(Object obj) { |
| 105 | if (this == obj) { |
| 106 | return true; |
| 107 | } |
| 108 | if (obj == null) { |
| 109 | return false; |
| 110 | } |
| 111 | if (getClass() != obj.getClass()) { |
| 112 | return false; |
| 113 | } |
| 114 | SkyKey other = (SkyKey) obj; |
Shreya Bhattarai | 956b01e | 2016-06-13 18:28:02 +0000 | [diff] [blame] | 115 | if (hashCode() != other.hashCode()) { |
| 116 | return false; |
| 117 | } |
Nathan Harmata | 09969fb | 2015-11-04 00:08:22 +0000 | [diff] [blame] | 118 | return functionName.equals(other.functionName) && argument.equals(other.argument); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | public static final Function<SkyKey, Object> NODE_NAME = new Function<SkyKey, Object>() { |
| 122 | @Override |
| 123 | public Object apply(SkyKey input) { |
| 124 | return input.argument(); |
| 125 | } |
| 126 | }; |
| 127 | } |