blob: 0af8b822500585f1653874517a9396d53ecbd3be [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
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.
14package com.google.devtools.build.skyframe;
15
16import com.google.common.base.Function;
Janak Ramakrishnanf745e992016-03-03 08:08:50 +000017import com.google.common.collect.Interner;
Nathan Harmataa16d9f12016-11-23 20:58:07 +000018import com.google.devtools.build.lib.concurrent.BlazeInterners;
Mark Schaller6df81792015-12-10 18:47:47 +000019import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020
21import java.io.Serializable;
22
23/**
24 * A {@link SkyKey} is effectively a pair (type, name) that identifies a Skyframe value.
25 */
26public final class SkyKey implements Serializable {
Nathan Harmataa16d9f12016-11-23 20:58:07 +000027 private static final Interner<SkyKey> SKY_KEY_INTERNER = BlazeInterners.newWeakInterner();
Janak Ramakrishnanf745e992016-03-03 08:08:50 +000028
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029 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 Schaller906f2552015-04-24 15:48:53 +000041 * 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 Nienhuysd08b27f2015-02-25 16:45:20 +010043 */
Mark Schaller906f2552015-04-24 15:48:53 +000044 private transient int hashCode;
45
Janak Ramakrishnanf745e992016-03-03 08:08:50 +000046 private SkyKey(SkyFunctionName functionName, Object argument) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010047 this.functionName = Preconditions.checkNotNull(functionName);
Janak Ramakrishnanf745e992016-03-03 08:08:50 +000048 this.argument = Preconditions.checkNotNull(argument);
Nathan Harmata59777352015-08-10 16:11:37 +000049 // '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 Nienhuysd08b27f2015-02-25 16:45:20 +010053 }
54
Janak Ramakrishnanf745e992016-03-03 08:08:50 +000055 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 Nienhuysd08b27f2015-02-25 16:45:20 +010060 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 Harmata59777352015-08-10 16:11:37 +000075 // 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 Harmata1b5c3272015-08-10 16:35:51 +000079 // 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 Harmata59777352015-08-10 16:11:37 +000081 //
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 Harmata87992dc2015-08-25 18:53:55 +000091 int h = hashCode;
92 if (h == 0) {
93 h = computeHashCode();
94 hashCode = h;
Mark Schaller906f2552015-04-24 15:48:53 +000095 }
Nathan Harmata87992dc2015-08-25 18:53:55 +000096 return h;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010097 }
98
Nathan Harmata59777352015-08-10 16:11:37 +000099 private int computeHashCode() {
100 return 31 * functionName.hashCode() + argument.hashCode();
Mark Schaller906f2552015-04-24 15:48:53 +0000101 }
102
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100103 @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 Bhattarai956b01e2016-06-13 18:28:02 +0000115 if (hashCode() != other.hashCode()) {
116 return false;
117 }
Nathan Harmata09969fb2015-11-04 00:08:22 +0000118 return functionName.equals(other.functionName) && argument.equals(other.argument);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100119 }
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}