blob: 35d2c70e82aeb36e6a36cd0d53bcc5d2ee4b1409 [file] [log] [blame]
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +00001// Copyright 2016 The Bazel Authors. All rights reserved.
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.
14package com.google.devtools.build.skyframe;
15
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +000016import com.google.common.base.Joiner;
Janak Ramakrishnan13221742016-06-15 16:37:22 +000017import com.google.common.base.MoreObjects;
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +000018import com.google.common.collect.Maps;
19import com.google.common.collect.Maps.EntryTransformer;
20import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
Janak Ramakrishnanf76c9592016-05-17 21:42:50 +000021import com.google.devtools.build.lib.util.GroupedList;
janakr3fd40542017-04-07 20:43:42 +000022import java.util.Collection;
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +000023import java.util.Map;
24import java.util.Set;
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +000025import javax.annotation.Nullable;
26
27/**
28 * Class that allows clients to be notified on each access of the graph. Clients can simply track
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000029 * accesses, or they can block to achieve desired synchronization. Clients should call {@link
30 * TrackingAwaiter#INSTANCE#assertNoErrors} at the end of tests in case exceptions were swallowed in
31 * async threads.
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +000032 */
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000033public class NotifyingHelper {
34 public static MemoizingEvaluator.GraphTransformerForTesting makeNotifyingTransformer(
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +000035 final Listener listener) {
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000036 return new MemoizingEvaluator.GraphTransformerForTesting() {
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +000037 @Override
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000038 public InMemoryGraph transform(InMemoryGraph graph) {
39 return new NotifyingInMemoryGraph(graph, listener);
40 }
41
42 @Override
Nathan Harmata33faf912016-08-10 19:15:15 +000043 public QueryableGraph transform(QueryableGraph graph) {
44 return new NotifyingQueryableGraph(graph, listener);
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000045 }
46
47 @Override
48 public ProcessableGraph transform(ProcessableGraph graph) {
49 return new NotifyingProcessableGraph(graph, listener);
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +000050 }
51 };
52 }
53
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000054 protected final Listener graphListener;
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +000055
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000056 protected final EntryTransformer<SkyKey, ThinNodeEntry, NodeEntry> wrapEntry =
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +000057 new EntryTransformer<SkyKey, ThinNodeEntry, NodeEntry>() {
58 @Nullable
59 @Override
60 public NotifyingNodeEntry transformEntry(SkyKey key, @Nullable ThinNodeEntry nodeEntry) {
61 return wrapEntry(key, nodeEntry);
62 }
63 };
64
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000065 NotifyingHelper(Listener graphListener) {
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +000066 this.graphListener = new ErrorRecordingDelegatingListener(graphListener);
67 }
68
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +000069 /** Subclasses should override if they wish to subclass NotifyingNodeEntry. */
70 @Nullable
71 protected NotifyingNodeEntry wrapEntry(SkyKey key, @Nullable ThinNodeEntry entry) {
72 return entry == null ? null : new NotifyingNodeEntry(key, entry);
73 }
74
Nathan Harmata33faf912016-08-10 19:15:15 +000075 static class NotifyingQueryableGraph implements QueryableGraph {
76 private final QueryableGraph delegate;
77 protected final NotifyingHelper notifyingHelper;
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000078
Nathan Harmata33faf912016-08-10 19:15:15 +000079 NotifyingQueryableGraph(QueryableGraph delegate, Listener graphListener) {
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000080 this.notifyingHelper = new NotifyingHelper(graphListener);
81 this.delegate = delegate;
82 }
83
Nathan Harmata33faf912016-08-10 19:15:15 +000084 NotifyingQueryableGraph(QueryableGraph delegate, NotifyingHelper helper) {
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000085 this.notifyingHelper = helper;
86 this.delegate = delegate;
87 }
88
89 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +000090 public Map<SkyKey, ? extends NodeEntry> getBatch(
ulfjackbe83f132017-07-18 18:12:09 +020091 @Nullable SkyKey requestor, Reason reason, Iterable<? extends SkyKey> keys)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +000092 throws InterruptedException {
Nathan Harmata3b47b1f2016-07-26 18:41:41 +000093 return Maps.transformEntries(
Nathan Harmata33faf912016-08-10 19:15:15 +000094 delegate.getBatch(requestor, reason, keys),
Nathan Harmata3b47b1f2016-07-26 18:41:41 +000095 notifyingHelper.wrapEntry);
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000096 }
Nathan Harmata33faf912016-08-10 19:15:15 +000097
98 @Nullable
99 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000100 public NodeEntry get(@Nullable SkyKey requestor, Reason reason, SkyKey key)
101 throws InterruptedException {
Nathan Harmata33faf912016-08-10 19:15:15 +0000102 return notifyingHelper.wrapEntry(key, delegate.get(requestor, reason, key));
103 }
Googler2dd4c182016-10-31 14:54:37 +0000104
105 @Override
106 public Iterable<SkyKey> getCurrentlyAvailableNodes(Iterable<SkyKey> keys, Reason reason) {
107 return delegate.getCurrentlyAvailableNodes(keys, reason);
108 }
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +0000109 }
110
Nathan Harmata33faf912016-08-10 19:15:15 +0000111 static class NotifyingProcessableGraph
112 extends NotifyingQueryableGraph implements ProcessableGraph {
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +0000113 protected final ProcessableGraph delegate;
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +0000114
115 NotifyingProcessableGraph(ProcessableGraph delegate, Listener graphListener) {
Nathan Harmata33faf912016-08-10 19:15:15 +0000116 this(delegate, new NotifyingHelper(graphListener));
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +0000117 }
118
119 NotifyingProcessableGraph(ProcessableGraph delegate, NotifyingHelper helper) {
Nathan Harmata33faf912016-08-10 19:15:15 +0000120 super(delegate, helper);
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +0000121 this.delegate = delegate;
122 }
123
124 @Override
125 public void remove(SkyKey key) {
126 delegate.remove(key);
127 }
128
129 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000130 public Map<SkyKey, ? extends NodeEntry> createIfAbsentBatch(
131 @Nullable SkyKey requestor, Reason reason, Iterable<SkyKey> keys)
132 throws InterruptedException {
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +0000133 for (SkyKey key : keys) {
134 notifyingHelper.graphListener.accept(key, EventType.CREATE_IF_ABSENT, Order.BEFORE, null);
135 }
Nathan Harmata3b47b1f2016-07-26 18:41:41 +0000136 return Maps.transformEntries(
137 delegate.createIfAbsentBatch(requestor, reason, keys),
138 notifyingHelper.wrapEntry);
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +0000139 }
janakr3fd40542017-04-07 20:43:42 +0000140
141 @Override
142 public DepsReport analyzeDepsDoneness(SkyKey parent, Collection<SkyKey> deps)
143 throws InterruptedException {
144 return delegate.analyzeDepsDoneness(parent, deps);
145 }
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +0000146 }
147
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000148 /**
149 * Graph/value entry events that the receiver can be informed of. When writing tests, feel free to
150 * add additional events here if needed.
151 */
152 public enum EventType {
153 CREATE_IF_ABSENT,
154 ADD_REVERSE_DEP,
155 REMOVE_REVERSE_DEP,
Janak Ramakrishnanf76c9592016-05-17 21:42:50 +0000156 GET_TEMPORARY_DIRECT_DEPS,
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000157 SIGNAL,
158 SET_VALUE,
159 MARK_DIRTY,
160 MARK_CLEAN,
161 IS_CHANGED,
162 GET_VALUE_WITH_METADATA,
163 IS_DIRTY,
164 IS_READY,
165 CHECK_IF_DONE,
166 GET_ALL_DIRECT_DEPS_FOR_INCOMPLETE_NODE
167 }
168
169 /**
170 * Whether the given event is about to happen or has just happened. For some events, both will be
171 * published, for others, only one. When writing tests, if you need an additional one to be
172 * published, feel free to add it.
173 */
174 public enum Order {
175 BEFORE,
176 AFTER
177 }
178
179 /** Receiver to be informed when an event for a given key occurs. */
180 public interface Listener {
181 @ThreadSafe
182 void accept(SkyKey key, EventType type, Order order, Object context);
183
184 Listener NULL_LISTENER =
185 new Listener() {
186 @Override
187 public void accept(SkyKey key, EventType type, Order order, Object context) {}
188 };
189 }
190
191 private static class ErrorRecordingDelegatingListener implements Listener {
192 private final Listener delegate;
193
194 private ErrorRecordingDelegatingListener(Listener delegate) {
195 this.delegate = delegate;
196 }
197
198 @Override
199 public void accept(SkyKey key, EventType type, Order order, Object context) {
200 try {
201 delegate.accept(key, type, order, context);
202 } catch (Exception e) {
203 TrackingAwaiter.INSTANCE.injectExceptionAndMessage(
204 e, "In NotifyingGraph: " + Joiner.on(", ").join(key, type, order, context));
205 throw e;
206 }
207 }
208 }
209
210 /** {@link NodeEntry} that informs a {@link Listener} of various method calls. */
211 protected class NotifyingNodeEntry extends DelegatingNodeEntry {
212 private final SkyKey myKey;
213 private final ThinNodeEntry delegate;
214
215 protected NotifyingNodeEntry(SkyKey key, ThinNodeEntry delegate) {
216 myKey = key;
217 this.delegate = delegate;
218 }
219
220 @Override
221 protected NodeEntry getDelegate() {
222 return (NodeEntry) delegate;
223 }
224
225 @Override
226 protected ThinNodeEntry getThinDelegate() {
227 return delegate;
228 }
229
230 @Override
Googler7a0b5182016-09-13 14:51:46 +0000231 public DependencyState addReverseDepAndCheckIfDone(SkyKey reverseDep)
232 throws InterruptedException {
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000233 graphListener.accept(myKey, EventType.ADD_REVERSE_DEP, Order.BEFORE, reverseDep);
234 DependencyState result = super.addReverseDepAndCheckIfDone(reverseDep);
235 graphListener.accept(myKey, EventType.ADD_REVERSE_DEP, Order.AFTER, reverseDep);
236 return result;
237 }
238
239 @Override
Googler7a0b5182016-09-13 14:51:46 +0000240 public void removeReverseDep(SkyKey reverseDep) throws InterruptedException {
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000241 graphListener.accept(myKey, EventType.REMOVE_REVERSE_DEP, Order.BEFORE, reverseDep);
242 super.removeReverseDep(reverseDep);
243 graphListener.accept(myKey, EventType.REMOVE_REVERSE_DEP, Order.AFTER, reverseDep);
244 }
245
246 @Override
Janak Ramakrishnanf76c9592016-05-17 21:42:50 +0000247 public GroupedList<SkyKey> getTemporaryDirectDeps() {
248 graphListener.accept(myKey, EventType.GET_TEMPORARY_DIRECT_DEPS, Order.BEFORE, null);
249 return super.getTemporaryDirectDeps();
250 }
251
252 @Override
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000253 public boolean signalDep(Version childVersion) {
254 graphListener.accept(myKey, EventType.SIGNAL, Order.BEFORE, childVersion);
255 boolean result = super.signalDep(childVersion);
256 graphListener.accept(myKey, EventType.SIGNAL, Order.AFTER, childVersion);
257 return result;
258 }
259
260 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000261 public Set<SkyKey> setValue(SkyValue value, Version version) throws InterruptedException {
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000262 graphListener.accept(myKey, EventType.SET_VALUE, Order.BEFORE, value);
263 Set<SkyKey> result = super.setValue(value, version);
264 graphListener.accept(myKey, EventType.SET_VALUE, Order.AFTER, value);
265 return result;
266 }
267
268 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000269 public MarkedDirtyResult markDirty(boolean isChanged) throws InterruptedException {
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000270 graphListener.accept(myKey, EventType.MARK_DIRTY, Order.BEFORE, isChanged);
271 MarkedDirtyResult result = super.markDirty(isChanged);
272 graphListener.accept(myKey, EventType.MARK_DIRTY, Order.AFTER, isChanged);
273 return result;
274 }
275
276 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000277 public Set<SkyKey> markClean() throws InterruptedException {
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000278 graphListener.accept(myKey, EventType.MARK_CLEAN, Order.BEFORE, this);
279 Set<SkyKey> result = super.markClean();
280 graphListener.accept(myKey, EventType.MARK_CLEAN, Order.AFTER, this);
281 return result;
282 }
283
284 @Override
285 public boolean isChanged() {
286 graphListener.accept(myKey, EventType.IS_CHANGED, Order.BEFORE, this);
287 return super.isChanged();
288 }
289
290 @Override
291 public boolean isDirty() {
292 graphListener.accept(myKey, EventType.IS_DIRTY, Order.BEFORE, this);
293 return super.isDirty();
294 }
295
296 @Override
297 public boolean isReady() {
298 graphListener.accept(myKey, EventType.IS_READY, Order.BEFORE, this);
299 return super.isReady();
300 }
301
302 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000303 public SkyValue getValueMaybeWithMetadata() throws InterruptedException {
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000304 graphListener.accept(myKey, EventType.GET_VALUE_WITH_METADATA, Order.BEFORE, this);
305 return super.getValueMaybeWithMetadata();
306 }
307
308 @Override
Googler7a0b5182016-09-13 14:51:46 +0000309 public DependencyState checkIfDoneForDirtyReverseDep(SkyKey reverseDep)
310 throws InterruptedException {
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000311 graphListener.accept(myKey, EventType.CHECK_IF_DONE, Order.BEFORE, reverseDep);
janakr468f1772018-08-10 23:36:52 -0700312 DependencyState dependencyState = super.checkIfDoneForDirtyReverseDep(reverseDep);
313 graphListener.accept(myKey, EventType.CHECK_IF_DONE, Order.AFTER, reverseDep);
314 return dependencyState;
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000315 }
316
317 @Override
Googler8d2311d2017-01-31 22:52:26 +0000318 public Iterable<SkyKey> getAllDirectDepsForIncompleteNode() throws InterruptedException {
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000319 graphListener.accept(
320 myKey, EventType.GET_ALL_DIRECT_DEPS_FOR_INCOMPLETE_NODE, Order.BEFORE, this);
321 return super.getAllDirectDepsForIncompleteNode();
322 }
Janak Ramakrishnan13221742016-06-15 16:37:22 +0000323
324 @Override
325 public String toString() {
326 return MoreObjects.toStringHelper(this).add("delegate", getThinDelegate()).toString();
327 }
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000328 }
329}