blob: a2efa1d22cd2d5b41abc4c161fcfca359183f040 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Nathan Harmatab408f9e2015-02-10 02:13:05 +00002//
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
jhorvitz402e80e2021-12-05 06:44:23 -080016import static com.google.common.base.MoreObjects.firstNonNull;
17
Googler2c19a572015-07-16 08:38:49 +000018import com.google.common.base.MoreObjects;
tomlua155b532017-11-08 20:12:47 +010019import com.google.common.base.Preconditions;
Nathan Harmatab408f9e2015-02-10 02:13:05 +000020import com.google.common.collect.ImmutableList;
21import com.google.common.collect.ImmutableSet;
22import com.google.devtools.build.lib.util.GroupedList;
23import com.google.devtools.build.lib.util.GroupedList.GroupedListHelper;
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +000024import com.google.devtools.build.skyframe.KeyToConsolidate.Op;
Googler9be33c82020-05-19 13:28:46 -070025import com.google.errorprone.annotations.ForOverride;
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +000026import java.util.ArrayList;
janakr3ea75592021-08-04 09:30:54 -070027import java.util.Collection;
Janak Ramakrishnan441dcb12015-10-09 22:44:31 +000028import java.util.List;
Nathan Harmatab408f9e2015-02-10 02:13:05 +000029import java.util.Set;
Nathan Harmatab408f9e2015-02-10 02:13:05 +000030import javax.annotation.Nullable;
31
32/**
33 * In-memory implementation of {@link NodeEntry}. All operations on this class are thread-safe.
34 *
35 * <p>Care was taken to provide certain compound operations to avoid certain check-then-act races.
36 * That means this class is somewhat closely tied to the exact Evaluator implementation.
37 *
38 * <p>Consider the example with two threads working on two nodes, where one depends on the other,
39 * say b depends on a. If a completes first, it's done. If it completes second, it needs to signal
40 * b, and potentially re-schedule it. If b completes first, it must exit, because it will be
41 * signaled (and re-scheduled) by a. If it completes second, it must signal (and re-schedule)
Janak Ramakrishnanab10f472017-03-24 17:08:24 +000042 * itself. However, if the Evaluator supported re-entrancy for a node, then this wouldn't have to be
43 * so strict, because duplicate scheduling would be less problematic.
Nathan Harmatab408f9e2015-02-10 02:13:05 +000044 *
Janak Ramakrishnanab10f472017-03-24 17:08:24 +000045 * <p>During its life, a node can go through states as follows:
46 *
47 * <ol>
48 * <li>Non-existent
ulfjack9beabe02019-02-13 23:41:59 -080049 * <li>Just created or marked as affected ({@link #isDone} is false; {@link #isDirty} is false)
50 * <li>Evaluating ({@link #isDone} is false; {@link #isDirty} is true)
51 * <li>Done ({@link #isDone} is true; {@link #isDirty} is false)
Janak Ramakrishnanab10f472017-03-24 17:08:24 +000052 * </ol>
53 *
54 * <p>The "just created" state is there to allow the {@link EvaluableGraph#createIfAbsentBatch} and
55 * {@link NodeEntry#addReverseDepAndCheckIfDone} methods to be separate. All callers have to call
Googler8a5b5632019-10-11 03:17:01 -070056 * both methods in that order if they want to create a node. The second method returns the
57 * NEEDS_SCHEDULING state only on the first time it was called. A caller that gets NEEDS_SCHEDULING
58 * back from that call must start the evaluation of this node, while any subsequent callers must
59 * not.
Janak Ramakrishnanab10f472017-03-24 17:08:24 +000060 *
Googler8a5b5632019-10-11 03:17:01 -070061 * <p>An entry is set to ALREADY_EVALUATING as soon as it is scheduled for evaluation. Thus, even a
62 * node that is never actually built (for instance, a dirty node that is verified as clean) is in
63 * the ALREADY_EVALUATING state until it is DONE.
Nathan Harmatac533acd2015-03-07 03:06:22 +000064 *
Googler8a5b5632019-10-11 03:17:01 -070065 * <p>From the DONE state, the node can go back to the "marked as affected" state.
ulfjack9beabe02019-02-13 23:41:59 -080066 *
Nathan Harmatab408f9e2015-02-10 02:13:05 +000067 * <p>This class is public only for the benefit of alternative graph implementations outside of the
68 * package.
69 */
Nathan Harmatac533acd2015-03-07 03:06:22 +000070public class InMemoryNodeEntry implements NodeEntry {
Nathan Harmatab408f9e2015-02-10 02:13:05 +000071
72 /** Actual data stored in this entry when it is done. */
janakrbf316f72018-11-28 14:38:44 -080073 protected volatile SkyValue value = null;
Nathan Harmatab408f9e2015-02-10 02:13:05 +000074
75 /**
Janak Ramakrishnan058c4ab2016-02-08 21:30:36 +000076 * The last version of the graph at which this node's value was changed. In {@link #setValue} it
77 * may be determined that the value being written to the graph at a given version is the same as
78 * the already-stored value. In that case, the version will remain the same. The version can be
79 * thought of as the latest timestamp at which this value was changed.
Nathan Harmatab408f9e2015-02-10 02:13:05 +000080 */
janakrbf316f72018-11-28 14:38:44 -080081 protected volatile Version lastChangedVersion = MinimalVersion.INSTANCE;
Janak Ramakrishnan1d22d4c2015-11-18 19:28:45 +000082
83 /**
84 * Returns the last version this entry was evaluated at, even if it re-evaluated to the same
Googler159a42a2018-10-31 13:19:51 -070085 * value. When a child signals this entry with the last version it was changed at in {@link
86 * #signalDep}, this entry need not re-evaluate if the child's version is at most this version,
87 * even if the {@link #lastChangedVersion} is less than this one.
Janak Ramakrishnan1d22d4c2015-11-18 19:28:45 +000088 *
Googler159a42a2018-10-31 13:19:51 -070089 * @see #signalDep(Version, SkyKey)
Janak Ramakrishnan1d22d4c2015-11-18 19:28:45 +000090 */
91 protected Version lastEvaluatedVersion = MinimalVersion.INSTANCE;
Nathan Harmatab408f9e2015-02-10 02:13:05 +000092
93 /**
Janak Ramakrishnan50934992016-07-06 17:09:51 +000094 * This object represents the direct deps of the node, in groups if the {@code SkyFunction}
janakr1cde8722017-10-10 03:22:21 +020095 * requested them that way. It contains either the in-progress direct deps, stored as a {@code
96 * GroupedList<SkyKey>} before the node is finished building, or the full direct deps, compressed
97 * in a memory-efficient way (via {@link GroupedList#compress}, after the node is done.
Janak Ramakrishnan50934992016-07-06 17:09:51 +000098 *
99 * <p>It is initialized lazily in getTemporaryDirectDeps() to save a little bit more memory.
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000100 */
janakr1cde8722017-10-10 03:22:21 +0200101 protected Object directDeps = null;
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000102
103 /**
104 * This list stores the reverse dependencies of this node that have been declared so far.
105 *
106 * <p>In case of a single object we store the object unwrapped, without the list, for
107 * memory-efficiency.
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000108 *
109 * <p>When an entry is being re-evaluated, this object stores the reverse deps from the previous
110 * evaluation. At the end of evaluation, the changed reverse dep operations from {@link
111 * #reverseDepsDataToConsolidate} are merged in here.
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000112 */
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000113 protected Object reverseDeps = ImmutableList.of();
114
115 /**
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000116 * This list stores objects returned by {@link KeyToConsolidate#create}. Morally they are {@link
117 * KeyToConsolidate} objects, but since some operations are stored bare, we can only declare that
118 * this list holds {@link Object} references. Created lazily to save memory.
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000119 *
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000120 * <p>This list serves double duty. For a done node, when a reverse dep is removed, checked for
121 * presence, or possibly added, we store the mutation in this object instead of immediately doing
122 * the operation. That is because removals/checks in reverseDeps are O(N). Originally reverseDeps
123 * was a HashSet, but because of memory consumption we switched to a list.
124 *
125 * <p>Internally, {@link ReverseDepsUtility} consolidates this data periodically, and when the set
126 * of reverse deps is requested. While this operation is not free, it can be done more effectively
Janak Ramakrishnan4f487f42015-11-23 18:51:55 +0000127 * than trying to remove/check each dirty reverse dependency individually (O(N) each time).
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000128 *
129 * <p>When the node entry is evaluating, this list serves to declare the reverse dep operations
130 * that have taken place on it during this evaluation. When evaluation finishes, this list will be
131 * merged into the existing reverse deps if any, but furthermore, this list will also be used to
132 * calculate the set of reverse deps to signal when this entry finishes evaluation. That is done
133 * by {@link ReverseDepsUtility#consolidateDataAndReturnNewElements}.
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000134 */
Janak Ramakrishnan441dcb12015-10-09 22:44:31 +0000135 private List<Object> reverseDepsDataToConsolidate = null;
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000136
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000137 /**
Janak Ramakrishnanab10f472017-03-24 17:08:24 +0000138 * Object encapsulating dirty state of the object between when it is marked dirty and
139 * re-evaluated.
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000140 */
janakr5b4f2372019-01-08 17:12:03 -0800141 @Nullable protected volatile DirtyBuildingState dirtyBuildingState = null;
Janak Ramakrishnanab10f472017-03-24 17:08:24 +0000142
Googlerbaefeab2019-04-30 17:12:55 -0700143 /** Construct a InMemoryNodeEntry. Use ONLY in Skyframe evaluation and graph implementations. */
144 public InMemoryNodeEntry() {}
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000145
janakr4f7be0f2017-10-18 11:45:48 -0400146 // Public only for use in alternate graph implementations.
janakr1cde8722017-10-10 03:22:21 +0200147 public KeepEdgesPolicy keepEdges() {
148 return KeepEdgesPolicy.ALL;
149 }
150
151 private boolean keepReverseDeps() {
152 return keepEdges() == KeepEdgesPolicy.ALL;
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000153 }
154
ulfjack9beabe02019-02-13 23:41:59 -0800155 private boolean isEvaluating() {
156 return dirtyBuildingState != null;
157 }
158
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000159 @Override
Shreya Bhattaraicc1b9b32017-01-06 18:19:25 +0000160 public boolean isDone() {
ulfjack9beabe02019-02-13 23:41:59 -0800161 return value != null && dirtyBuildingState == null;
162 }
163
164 @Override
165 public synchronized boolean isReady() {
166 Preconditions.checkState(!isDone(), "can't be ready if done: %s", this);
167 Preconditions.checkState(isEvaluating(), this);
168 return dirtyBuildingState.isReady(getNumTemporaryDirectDeps());
169 }
170
171 @Override
172 public synchronized boolean isDirty() {
173 return !isDone() && dirtyBuildingState != null;
174 }
175
176 @Override
177 public synchronized boolean isChanged() {
178 return !isDone() && dirtyBuildingState != null && dirtyBuildingState.isChanged();
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000179 }
180
181 @Override
janakr19e03d72018-01-10 13:13:00 -0800182 public SkyValue getValue() {
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000183 Preconditions.checkState(isDone(), "no value until done. ValueEntry: %s", this);
184 return ValueWithMetadata.justValue(value);
185 }
186
187 @Override
mschallera8926b72018-06-28 11:53:36 -0700188 @Nullable
janakr19e03d72018-01-10 13:13:00 -0800189 public SkyValue getValueMaybeWithMetadata() {
Janak Ramakrishnan0afd4532015-08-24 17:27:48 +0000190 return value;
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000191 }
192
193 @Override
janakr19e03d72018-01-10 13:13:00 -0800194 public SkyValue toValue() {
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000195 if (isDone()) {
196 return getErrorInfo() == null ? getValue() : null;
197 } else if (isChanged() || isDirty()) {
jhorvitz2292db92021-11-30 16:02:28 -0800198 SkyValue lastBuildValue;
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000199 try {
ulfjack9beabe02019-02-13 23:41:59 -0800200 lastBuildValue = dirtyBuildingState.getLastBuildValue();
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000201 } catch (InterruptedException e) {
202 throw new IllegalStateException("Interruption unexpected: " + this, e);
203 }
jhorvitz2292db92021-11-30 16:02:28 -0800204 return ValueWithMetadata.justValue(lastBuildValue);
Janak Ramakrishnan54111292015-09-09 21:44:07 +0000205 } else {
206 // Value has not finished evaluating. It's probably about to be cleaned from the graph.
207 return null;
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000208 }
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000209 }
210
211 @Override
Googlerb808db42019-05-14 16:32:30 -0700212 public Iterable<SkyKey> getDirectDeps() {
janakr4d6c0ab2019-04-29 14:46:53 -0700213 return GroupedList.compressedToIterable(getCompressedDirectDepsForDoneEntry());
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000214 }
215
Googlerb808db42019-05-14 16:32:30 -0700216 @Override
Googleref0f8e62020-04-19 09:40:53 -0700217 public boolean hasAtLeastOneDep() {
218 return GroupedList.numGroups(getCompressedDirectDepsForDoneEntry()) > 0;
Googlerb808db42019-05-14 16:32:30 -0700219 }
220
janakr4d6c0ab2019-04-29 14:46:53 -0700221 /** Returns the compressed {@link GroupedList} of direct deps. Can only be called when done. */
Googlerb808db42019-05-14 16:32:30 -0700222 public final synchronized @GroupedList.Compressed Object getCompressedDirectDepsForDoneEntry() {
janakr1cde8722017-10-10 03:22:21 +0200223 assertKeepDeps();
Janak Ramakrishnan50934992016-07-06 17:09:51 +0000224 Preconditions.checkState(isDone(), "no deps until done. NodeEntry: %s", this);
Googlerbaefeab2019-04-30 17:12:55 -0700225 Preconditions.checkNotNull(directDeps, "deps can't be null: %s", this);
226 return GroupedList.castAsCompressed(directDeps);
Nathan Harmatadf22aaf2015-03-06 20:44:46 +0000227 }
228
shreyax5d634362017-11-29 11:31:49 -0800229 public int getNumDirectDeps() {
Googlerbaefeab2019-04-30 17:12:55 -0700230 return GroupedList.numElements(getCompressedDirectDepsForDoneEntry());
shreyax5d634362017-11-29 11:31:49 -0800231 }
232
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000233 @Override
234 @Nullable
235 public synchronized ErrorInfo getErrorInfo() {
Janak Ramakrishnan50934992016-07-06 17:09:51 +0000236 Preconditions.checkState(isDone(), "no errors until done. NodeEntry: %s", this);
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000237 return ValueWithMetadata.getMaybeErrorInfo(value);
238 }
239
Janak Ramakrishnan4f487f42015-11-23 18:51:55 +0000240 /**
Janak Ramakrishnan772b5bb2016-06-29 00:20:36 +0000241 * Puts entry in "done" state, as checked by {@link #isDone}. Subclasses that override one may
242 * need to override the other.
Janak Ramakrishnan4f487f42015-11-23 18:51:55 +0000243 */
244 protected void markDone() {
Janak Ramakrishnanab10f472017-03-24 17:08:24 +0000245 dirtyBuildingState = null;
Janak Ramakrishnan4f487f42015-11-23 18:51:55 +0000246 }
247
ulfjack20eeaad2019-02-18 02:51:55 -0800248 @Override
249 public synchronized void addExternalDep() {
250 Preconditions.checkNotNull(dirtyBuildingState, this);
251 dirtyBuildingState.addExternalDep();
252 }
253
janakr1cde8722017-10-10 03:22:21 +0200254 protected final synchronized Set<SkyKey> setStateFinishedAndReturnReverseDepsToSignal() {
jhorvitzdd1235f2021-12-10 09:39:53 -0800255 Set<SkyKey> reverseDepsToSignal = ReverseDepsUtility.consolidateDataAndReturnNewElements(this);
256 directDeps = keepEdges() == KeepEdgesPolicy.NONE ? null : getTemporaryDirectDeps().compress();
Janak Ramakrishnan4f487f42015-11-23 18:51:55 +0000257 markDone();
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000258 return reverseDepsToSignal;
259 }
260
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000261 @Override
262 public synchronized Set<SkyKey> getInProgressReverseDeps() {
263 Preconditions.checkState(!isDone(), this);
jhorvitzdd1235f2021-12-10 09:39:53 -0800264 return ReverseDepsUtility.returnNewElements(this);
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000265 }
266
jhorvitz2292db92021-11-30 16:02:28 -0800267 /**
268 * {@inheritDoc}
269 *
270 * <p>In this method it is crucial that {@link #lastChangedVersion} is set prior to {@link #value}
271 * because although this method itself is synchronized, there are unsynchronized consumers of the
272 * version and the value.
273 */
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000274 @Override
jhorvitz402e80e2021-12-05 06:44:23 -0800275 public synchronized Set<SkyKey> setValue(
276 SkyValue value, Version graphVersion, @Nullable Version maxTransitiveSourceVersion)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000277 throws InterruptedException {
jhorvitz2292db92021-11-30 16:02:28 -0800278 Preconditions.checkState(isReady(), "Not ready (this=%s, value=%s)", this, value);
jhorvitz402e80e2021-12-05 06:44:23 -0800279 Version version = firstNonNull(maxTransitiveSourceVersion, graphVersion);
jhorvitz2292db92021-11-30 16:02:28 -0800280 Preconditions.checkState(
281 this.lastChangedVersion.atMost(version) && this.lastEvaluatedVersion.atMost(version),
282 "Bad version (this=%s, version=%s, value=%s)",
283 this,
284 version,
285 value);
Janak Ramakrishnan1d22d4c2015-11-18 19:28:45 +0000286 this.lastEvaluatedVersion = version;
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000287
jhorvitz2292db92021-11-30 16:02:28 -0800288 if (dirtyBuildingState.unchangedFromLastBuild(value)) {
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000289 // If the value is the same as before, just use the old value. Note that we don't use the new
290 // value, because preserving == equality is even better than .equals() equality.
ulfjack9beabe02019-02-13 23:41:59 -0800291 this.value = dirtyBuildingState.getLastBuildValue();
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000292 } else {
shahand4a6ca02018-10-03 11:41:28 -0700293 // If this is a new value, or it has changed since the last build, set the version to the
294 // current graph version.
Janak Ramakrishnan1d22d4c2015-11-18 19:28:45 +0000295 this.lastChangedVersion = version;
Googler1c121702018-11-01 14:04:09 -0700296 this.value = value;
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000297 }
Janak Ramakrishnan772b5bb2016-06-29 00:20:36 +0000298 return setStateFinishedAndReturnReverseDepsToSignal();
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000299 }
300
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000301 @Override
janakr3b6274c2021-06-23 07:31:32 -0700302 public DependencyState addReverseDepAndCheckIfDone(SkyKey reverseDep) {
303 if ((reverseDep == null || !keepReverseDeps()) && isDone()) {
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000304 return DependencyState.DONE;
305 }
janakr3b6274c2021-06-23 07:31:32 -0700306
307 synchronized (this) {
308 boolean done = isDone();
jhorvitzdd1235f2021-12-10 09:39:53 -0800309 if (!done && dirtyBuildingState == null) {
310 dirtyBuildingState = DirtyBuildingState.createNew();
311 }
janakr3b6274c2021-06-23 07:31:32 -0700312 if (reverseDep != null) {
313 if (done) {
314 if (keepReverseDeps()) {
315 ReverseDepsUtility.addReverseDep(this, reverseDep);
316 }
317 } else {
318 appendToReverseDepOperations(reverseDep, Op.ADD);
319 }
320 }
321 if (done) {
322 return DependencyState.DONE;
323 }
janakr3b6274c2021-06-23 07:31:32 -0700324 boolean wasEvaluating = dirtyBuildingState.isEvaluating();
325 if (!wasEvaluating) {
326 dirtyBuildingState.startEvaluating();
327 }
328 return wasEvaluating ? DependencyState.ALREADY_EVALUATING : DependencyState.NEEDS_SCHEDULING;
ulfjack9beabe02019-02-13 23:41:59 -0800329 }
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000330 }
331
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000332 /** Sets {@link #reverseDeps}. Does not alter {@link #reverseDepsDataToConsolidate}. */
333 synchronized void setSingleReverseDepForReverseDepsUtil(SkyKey reverseDep) {
334 this.reverseDeps = reverseDep;
335 }
336
337 /** Sets {@link #reverseDeps}. Does not alter {@link #reverseDepsDataToConsolidate}. */
338 synchronized void setReverseDepsForReverseDepsUtil(List<SkyKey> reverseDeps) {
339 this.reverseDeps = reverseDeps;
340 }
341
342 /** Sets {@link #reverseDepsDataToConsolidate}. Does not alter {@link #reverseDeps}. */
343 synchronized void setReverseDepsDataToConsolidateForReverseDepsUtil(
344 List<Object> dataToConsolidate) {
345 this.reverseDepsDataToConsolidate = dataToConsolidate;
346 }
347
348 synchronized Object getReverseDepsRawForReverseDepsUtil() {
349 return this.reverseDeps;
350 }
351
352 synchronized List<Object> getReverseDepsDataToConsolidateForReverseDepsUtil() {
353 return this.reverseDepsDataToConsolidate;
354 }
355
356 private synchronized void appendToReverseDepOperations(SkyKey reverseDep, Op op) {
357 Preconditions.checkState(!isDone(), "Don't append to done %s %s %s", this, reverseDep, op);
358 if (reverseDepsDataToConsolidate == null) {
359 reverseDepsDataToConsolidate = new ArrayList<>();
360 }
361 Preconditions.checkState(
362 isDirty() || op != Op.CHECK, "Not dirty check %s %s", this, reverseDep);
jhorvitzdd1235f2021-12-10 09:39:53 -0800363 reverseDepsDataToConsolidate.add(KeyToConsolidate.create(reverseDep, op, this));
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000364 }
365
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000366 @Override
367 public synchronized DependencyState checkIfDoneForDirtyReverseDep(SkyKey reverseDep) {
368 Preconditions.checkNotNull(reverseDep, this);
janakr1cde8722017-10-10 03:22:21 +0200369 // Note that implementations of InMemoryNodeEntry that have
370 // #keepEdges == KeepEdgesPolicy.JUST_DEPS may override this entire method.
371 Preconditions.checkState(
372 keepEdges() == KeepEdgesPolicy.ALL,
373 "Incremental means keeping edges %s %s",
374 reverseDep,
375 this);
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000376 if (isDone()) {
377 ReverseDepsUtility.checkReverseDep(this, reverseDep);
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000378 } else {
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000379 appendToReverseDepOperations(reverseDep, Op.CHECK);
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000380 }
381 return addReverseDepAndCheckIfDone(null);
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000382 }
383
384 @Override
385 public synchronized void removeReverseDep(SkyKey reverseDep) {
janakr1cde8722017-10-10 03:22:21 +0200386 if (!keepReverseDeps()) {
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000387 return;
388 }
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000389 if (isDone()) {
390 ReverseDepsUtility.removeReverseDep(this, reverseDep);
391 } else {
392 // Removing a reverse dep from an in-flight node is rare -- it should only happen when this
393 // node is about to be cleaned from the graph.
394 appendToReverseDepOperations(reverseDep, Op.REMOVE_OLD);
395 }
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000396 }
397
398 @Override
janakrd86762c2021-08-06 13:22:43 -0700399 public synchronized void removeReverseDepsFromDoneEntryDueToDeletion(Set<SkyKey> deletedKeys) {
400 assertKeepRdeps();
401 Preconditions.checkState(isDone(), this);
402 ReverseDepsUtility.removeReverseDepsMatching(this, deletedKeys);
403 }
404
405 @Override
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000406 public synchronized void removeInProgressReverseDep(SkyKey reverseDep) {
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000407 appendToReverseDepOperations(reverseDep, Op.REMOVE);
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000408 }
409
410 @Override
janakr3ea75592021-08-04 09:30:54 -0700411 public synchronized Collection<SkyKey> getReverseDepsForDoneEntry() {
janakr1cde8722017-10-10 03:22:21 +0200412 assertKeepRdeps();
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000413 Preconditions.checkState(isDone(), "Called on not done %s", this);
janakr3ea75592021-08-04 09:30:54 -0700414 return ReverseDepsUtility.getReverseDeps(this, /*checkConsistency=*/ true);
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000415 }
416
417 @Override
janakr3ea75592021-08-04 09:30:54 -0700418 public synchronized Collection<SkyKey> getAllReverseDepsForNodeBeingDeleted() {
janakr1cde8722017-10-10 03:22:21 +0200419 assertKeepRdeps();
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000420 if (!isDone()) {
421 // This consolidation loses information about pending reverse deps to signal, but that is
422 // unimportant since this node is being deleted.
jhorvitzdd1235f2021-12-10 09:39:53 -0800423 ReverseDepsUtility.consolidateDataAndReturnNewElements(this);
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000424 }
janakr3ea75592021-08-04 09:30:54 -0700425 return ReverseDepsUtility.getReverseDeps(this, /*checkConsistency=*/ false);
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000426 }
427
428 @Override
shahan44666dc2018-10-05 17:47:11 -0700429 public synchronized boolean signalDep(Version childVersion, @Nullable SkyKey childForDebugging) {
430 Preconditions.checkState(
431 !isDone(), "Value must not be done in signalDep %s child=%s", this, childForDebugging);
ulfjack9beabe02019-02-13 23:41:59 -0800432 Preconditions.checkNotNull(dirtyBuildingState, "%s %s", this, childForDebugging);
433 Preconditions.checkState(dirtyBuildingState.isEvaluating(), "%s %s", this, childForDebugging);
434 dirtyBuildingState.signalDep();
jhorvitz2292db92021-11-30 16:02:28 -0800435
436 // childVersion > lastEvaluatedVersion means the child has changed since the last evaluation.
437 boolean childChanged = !childVersion.atMost(lastEvaluatedVersion);
438 dirtyBuildingState.signalDepPostProcess(childChanged, getNumTemporaryDirectDeps());
Janak Ramakrishnanab10f472017-03-24 17:08:24 +0000439 return isReady();
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000440 }
441
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000442 /** Checks that a caller is not trying to access not-stored graph edges. */
janakr1cde8722017-10-10 03:22:21 +0200443 private void assertKeepDeps() {
444 Preconditions.checkState(keepEdges() != KeepEdgesPolicy.NONE, "Not keeping deps: %s", this);
445 }
446
447 /** Checks that a caller is not trying to access not-stored graph edges. */
448 private void assertKeepRdeps() {
449 Preconditions.checkState(keepEdges() == KeepEdgesPolicy.ALL, "Not keeping rdeps: %s", this);
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000450 }
451
Googler9be33c82020-05-19 13:28:46 -0700452 /**
453 * Creates a {@link DirtyBuildingState} for the case where this node is done and is being marked
454 * dirty.
455 */
456 @ForOverride
457 protected DirtyBuildingState createDirtyBuildingStateForDoneNode(
458 DirtyType dirtyType, GroupedList<SkyKey> directDeps, SkyValue value) {
459 return DirtyBuildingState.create(dirtyType, directDeps, value);
460 }
461
janakr6c3e9832020-08-04 17:32:24 -0700462 private static final GroupedList<SkyKey> EMPTY_LIST = new GroupedList<>();
463
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000464 @Override
mschaller7138b072018-09-28 10:14:11 -0700465 public synchronized MarkedDirtyResult markDirty(DirtyType dirtyType) {
janakr6c3e9832020-08-04 17:32:24 -0700466 if (!DirtyType.FORCE_REBUILD.equals(dirtyType)) {
467 // A node can't be found to be dirty without deps unless it's force-rebuilt.
468 assertKeepDeps();
469 }
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000470 if (isDone()) {
janakr6c3e9832020-08-04 17:32:24 -0700471 GroupedList<SkyKey> directDeps =
472 KeepEdgesPolicy.NONE.equals(keepEdges())
473 ? EMPTY_LIST
474 : GroupedList.create(getCompressedDirectDepsForDoneEntry());
475 dirtyBuildingState = createDirtyBuildingStateForDoneNode(dirtyType, directDeps, value);
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000476 value = null;
janakr6c3e9832020-08-04 17:32:24 -0700477 this.directDeps = null;
478 return new MarkedDirtyResult(
479 KeepEdgesPolicy.ALL.equals(keepEdges())
janakr3ea75592021-08-04 09:30:54 -0700480 ? ReverseDepsUtility.getReverseDeps(this, /*checkConsistency=*/ true)
janakr6c3e9832020-08-04 17:32:24 -0700481 : ImmutableList.of());
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000482 }
mschaller7138b072018-09-28 10:14:11 -0700483 if (dirtyType.equals(DirtyType.FORCE_REBUILD)) {
mschaller0d7c71a2019-03-05 08:50:21 -0800484 if (dirtyBuildingState != null) {
485 dirtyBuildingState.markForceRebuild();
486 }
mschaller7138b072018-09-28 10:14:11 -0700487 return null;
488 }
489 // The caller may be simultaneously trying to mark this node dirty and changed, and the dirty
490 // thread may have lost the race, but it is the caller's responsibility not to try to mark
491 // this node changed twice. The end result of racing markers must be a changed node, since one
492 // of the markers is trying to mark the node changed.
493 Preconditions.checkState(
494 dirtyType.equals(DirtyType.CHANGE) != isChanged(),
495 "Cannot mark node dirty twice or changed twice: %s",
496 this);
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000497 Preconditions.checkState(value == null, "Value should have been reset already %s", this);
mschaller7138b072018-09-28 10:14:11 -0700498 if (dirtyType.equals(DirtyType.CHANGE)) {
ulfjack9beabe02019-02-13 23:41:59 -0800499 Preconditions.checkNotNull(dirtyBuildingState);
mschaller7138b072018-09-28 10:14:11 -0700500 // If the changed marker lost the race, we just need to mark changed in this method -- all
501 // other work was done by the dirty marker.
ulfjack9beabe02019-02-13 23:41:59 -0800502 dirtyBuildingState.markChanged();
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000503 }
mschaller7138b072018-09-28 10:14:11 -0700504 return null;
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000505 }
506
507 @Override
Googlercbbb5392019-08-30 08:31:28 -0700508 public synchronized NodeValueAndRdepsToSignal markClean() throws InterruptedException {
ulfjack9beabe02019-02-13 23:41:59 -0800509 Preconditions.checkNotNull(dirtyBuildingState, this);
510 this.value = Preconditions.checkNotNull(dirtyBuildingState.getLastBuildValue());
Janak Ramakrishnan50934992016-07-06 17:09:51 +0000511 Preconditions.checkState(isReady(), "Should be ready when clean: %s", this);
512 Preconditions.checkState(
ulfjack9beabe02019-02-13 23:41:59 -0800513 dirtyBuildingState.depsUnchangedFromLastBuild(getTemporaryDirectDeps()),
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000514 "Direct deps must be the same as those found last build for node to be marked clean: %s",
515 this);
516 Preconditions.checkState(isDirty(), this);
Janak Ramakrishnanab10f472017-03-24 17:08:24 +0000517 Preconditions.checkState(!dirtyBuildingState.isChanged(), "shouldn't be changed: %s", this);
Googlercbbb5392019-08-30 08:31:28 -0700518 Set<SkyKey> rDepsToSignal = setStateFinishedAndReturnReverseDepsToSignal();
jhorvitz2292db92021-11-30 16:02:28 -0800519 return new NodeValueAndRdepsToSignal(this.value, rDepsToSignal);
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000520 }
521
522 @Override
523 public synchronized void forceRebuild() {
ulfjack9beabe02019-02-13 23:41:59 -0800524 Preconditions.checkNotNull(dirtyBuildingState, this);
525 Preconditions.checkState(isEvaluating(), this);
526 dirtyBuildingState.forceRebuild(getNumTemporaryDirectDeps());
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000527 }
528
529 @Override
Googlerc5a2f812018-08-21 14:08:59 -0700530 public Version getVersion() {
Janak Ramakrishnan1d22d4c2015-11-18 19:28:45 +0000531 return lastChangedVersion;
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000532 }
533
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000534 @Override
535 public synchronized NodeEntry.DirtyState getDirtyState() {
ulfjack9beabe02019-02-13 23:41:59 -0800536 Preconditions.checkNotNull(dirtyBuildingState, this);
537 return dirtyBuildingState.getDirtyState();
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000538 }
539
Janak Ramakrishnan5dddb002016-07-06 20:07:23 +0000540 /** @see DirtyBuildingState#getNextDirtyDirectDeps() */
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000541 @Override
janakrad1c2e42019-02-08 14:00:03 -0800542 public synchronized List<SkyKey> getNextDirtyDirectDeps() throws InterruptedException {
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000543 Preconditions.checkState(isReady(), this);
ulfjack9beabe02019-02-13 23:41:59 -0800544 Preconditions.checkNotNull(dirtyBuildingState, this);
545 Preconditions.checkState(
546 dirtyBuildingState.isEvaluating(), "Not evaluating during getNextDirty? %s", this);
547 return dirtyBuildingState.getNextDirtyDirectDeps();
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000548 }
549
550 @Override
Googler8d2311d2017-01-31 22:52:26 +0000551 public synchronized Iterable<SkyKey> getAllDirectDepsForIncompleteNode()
552 throws InterruptedException {
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000553 Preconditions.checkState(!isDone(), this);
554 if (!isDirty()) {
shreyaxa6679ae2018-03-02 15:59:46 -0800555 return getTemporaryDirectDeps().getAllElementsAsIterable();
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000556 } else {
Janak Ramakrishnanf76c9592016-05-17 21:42:50 +0000557 // There may be duplicates here. Make sure everything is unique.
558 ImmutableSet.Builder<SkyKey> result = ImmutableSet.builder();
559 for (Iterable<SkyKey> group : getTemporaryDirectDeps()) {
560 result.addAll(group);
561 }
ulfjack9beabe02019-02-13 23:41:59 -0800562 result.addAll(dirtyBuildingState.getAllRemainingDirtyDirectDeps(/*preservePosition=*/ false));
Janak Ramakrishnanf76c9592016-05-17 21:42:50 +0000563 return result.build();
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000564 }
565 }
566
567 @Override
janakr3cb0c392019-03-29 20:08:41 -0700568 public synchronized ImmutableSet<SkyKey> getAllRemainingDirtyDirectDeps()
569 throws InterruptedException {
ulfjack9beabe02019-02-13 23:41:59 -0800570 Preconditions.checkNotNull(dirtyBuildingState, this);
571 Preconditions.checkState(
572 dirtyBuildingState.isEvaluating(), "Not evaluating for remaining dirty? %s", this);
Janak Ramakrishnanf76c9592016-05-17 21:42:50 +0000573 if (isDirty()) {
ulfjack9beabe02019-02-13 23:41:59 -0800574 DirtyState dirtyState = dirtyBuildingState.getDirtyState();
Janak Ramakrishnan5dddb002016-07-06 20:07:23 +0000575 Preconditions.checkState(
janakre54491e2018-07-11 16:29:13 -0700576 dirtyState == DirtyState.REBUILDING || dirtyState == DirtyState.FORCED_REBUILDING, this);
ulfjack9beabe02019-02-13 23:41:59 -0800577 return dirtyBuildingState.getAllRemainingDirtyDirectDeps(/*preservePosition=*/ true);
Janak Ramakrishnanf76c9592016-05-17 21:42:50 +0000578 } else {
Janak Ramakrishnanf76c9592016-05-17 21:42:50 +0000579 return ImmutableSet.of();
580 }
581 }
582
583 @Override
584 public synchronized void markRebuilding() {
jhorvitz2292db92021-11-30 16:02:28 -0800585 Preconditions.checkNotNull(dirtyBuildingState, this).markRebuilding();
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000586 }
587
Janak Ramakrishnan50934992016-07-06 17:09:51 +0000588 @SuppressWarnings("unchecked")
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000589 @Override
Janak Ramakrishnan3b5d5d22016-05-13 21:14:56 +0000590 public synchronized GroupedList<SkyKey> getTemporaryDirectDeps() {
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000591 Preconditions.checkState(!isDone(), "temporary shouldn't be done: %s", this);
Janak Ramakrishnan50934992016-07-06 17:09:51 +0000592 if (directDeps == null) {
593 // Initialize lazily, to save a little bit of memory.
jhorvitz2292db92021-11-30 16:02:28 -0800594 directDeps = new GroupedList<>();
Janak Ramakrishnan50934992016-07-06 17:09:51 +0000595 }
596 return (GroupedList<SkyKey>) directDeps;
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000597 }
598
felly44eb9642018-03-27 12:41:13 -0700599 private synchronized int getNumTemporaryDirectDeps() {
600 return directDeps == null ? 0 : getTemporaryDirectDeps().numElements();
601 }
602
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000603 @Override
604 public synchronized boolean noDepsLastBuild() {
ulfjack9beabe02019-02-13 23:41:59 -0800605 Preconditions.checkState(isEvaluating(), this);
606 return dirtyBuildingState.noDepsLastBuild();
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000607 }
608
Janak Ramakrishnan50934992016-07-06 17:09:51 +0000609 /**
610 * {@inheritDoc}
611 *
612 * <p>This is complicated by the need to maintain the group data. If we remove a dep that ended a
613 * group, then its predecessor's group data must be changed to indicate that it now ends the
614 * group.
615 */
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000616 @Override
617 public synchronized void removeUnfinishedDeps(Set<SkyKey> unfinishedDeps) {
Janak Ramakrishnan50934992016-07-06 17:09:51 +0000618 getTemporaryDirectDeps().remove(unfinishedDeps);
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000619 }
620
621 @Override
janakr46706ae2018-04-30 16:18:50 -0700622 public synchronized void resetForRestartFromScratch() {
mschalleraf4493c2019-05-08 12:04:47 -0700623 Preconditions.checkState(isReady(), this);
janakr46706ae2018-04-30 16:18:50 -0700624 directDeps = null;
ulfjack9beabe02019-02-13 23:41:59 -0800625 dirtyBuildingState.resetForRestartFromScratch();
janakr46706ae2018-04-30 16:18:50 -0700626 }
627
628 @Override
Janak Ramakrishnan2b8a3a42016-10-14 11:41:27 +0000629 public synchronized Set<SkyKey> addTemporaryDirectDeps(GroupedListHelper<SkyKey> helper) {
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000630 Preconditions.checkState(!isDone(), "add temp shouldn't be done: %s %s", helper, this);
Janak Ramakrishnan2b8a3a42016-10-14 11:41:27 +0000631 return getTemporaryDirectDeps().append(helper);
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000632 }
633
634 @Override
janakrad1c2e42019-02-08 14:00:03 -0800635 public synchronized void addTemporaryDirectDepsGroupToDirtyEntry(List<SkyKey> group) {
Mark Schallerd1cd14b2015-12-09 16:23:22 +0000636 Preconditions.checkState(!isDone(), "add group temp shouldn't be done: %s %s", group, this);
Janak Ramakrishnan50934992016-07-06 17:09:51 +0000637 getTemporaryDirectDeps().appendGroup(group);
Mark Schallerd1cd14b2015-12-09 16:23:22 +0000638 }
639
janakra2d4d3d2018-12-10 18:30:08 -0800640 protected synchronized MoreObjects.ToStringHelper toStringHelper() {
Googler2c19a572015-07-16 08:38:49 +0000641 return MoreObjects.toStringHelper(this)
Janak Ramakrishnan5877b8b2015-09-22 17:37:10 +0000642 .add("identity", System.identityHashCode(this))
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000643 .add("value", value)
Janak Ramakrishnan1d22d4c2015-11-18 19:28:45 +0000644 .add("lastChangedVersion", lastChangedVersion)
645 .add("lastEvaluatedVersion", lastEvaluatedVersion)
janakrdd4c47f2019-02-08 17:03:19 -0800646 .add(
647 "directDeps",
648 isDone() && keepEdges() != KeepEdgesPolicy.NONE
Googlerbaefeab2019-04-30 17:12:55 -0700649 ? GroupedList.create(getCompressedDirectDepsForDoneEntry())
janakrdd4c47f2019-02-08 17:03:19 -0800650 : directDeps)
Janak Ramakrishnancb8a97d2017-03-23 20:50:08 +0000651 .add("reverseDeps", ReverseDepsUtility.toString(this))
janakra2d4d3d2018-12-10 18:30:08 -0800652 .add("dirtyBuildingState", dirtyBuildingState);
653 }
654
655 @Override
656 public final synchronized String toString() {
657 return toStringHelper().toString();
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000658 }
659
janakr3b6274c2021-06-23 07:31:32 -0700660 // Only used for testing hooks.
janakr1cde8722017-10-10 03:22:21 +0200661 protected synchronized InMemoryNodeEntry cloneNodeEntry(InMemoryNodeEntry newEntry) {
janakr1cde8722017-10-10 03:22:21 +0200662 Preconditions.checkState(isDone(), "Only done nodes can be copied: %s", this);
663 newEntry.value = value;
664 newEntry.lastChangedVersion = this.lastChangedVersion;
665 newEntry.lastEvaluatedVersion = this.lastEvaluatedVersion;
janakr3ea75592021-08-04 09:30:54 -0700666 for (SkyKey reverseDep : ReverseDepsUtility.getReverseDeps(this, /*checkConsistency=*/ true)) {
janakr3b6274c2021-06-23 07:31:32 -0700667 ReverseDepsUtility.addReverseDep(newEntry, reverseDep);
668 }
janakr1cde8722017-10-10 03:22:21 +0200669 newEntry.directDeps = directDeps;
670 newEntry.dirtyBuildingState = null;
671 return newEntry;
672 }
673
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000674 /**
675 * Do not use except in custom evaluator implementations! Added only temporarily.
676 *
677 * <p>Clones a InMemoryMutableNodeEntry iff it is a done node. Otherwise it fails.
678 */
679 public synchronized InMemoryNodeEntry cloneNodeEntry() {
janakr1cde8722017-10-10 03:22:21 +0200680 return cloneNodeEntry(new InMemoryNodeEntry());
Nathan Harmatab408f9e2015-02-10 02:13:05 +0000681 }
682}