| // Copyright 2014 Google Inc. 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.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; | |
| import com.google.devtools.build.lib.util.Pair; | |
| import java.util.Set; | |
| /** | |
| * Class that allows clients to be notified on each access of the graph. Clients can simply track | |
| * accesses, or they can block to achieve desired synchronization. | |
| */ | |
| public class NotifyingInMemoryGraph extends InMemoryGraph { | |
| private final Listener graphListener; | |
| public NotifyingInMemoryGraph(Listener graphListener) { | |
| this.graphListener = graphListener; | |
| } | |
| @Override | |
| public NodeEntry createIfAbsent(SkyKey key) { | |
| graphListener.accept(key, EventType.CREATE_IF_ABSENT, Order.BEFORE, null); | |
| NodeEntry newval = getEntry(key); | |
| NodeEntry oldval = getNodeMap().putIfAbsent(key, newval); | |
| return oldval == null ? newval : oldval; | |
| } | |
| // Subclasses should override if they wish to subclass NotifyingNodeEntry. | |
| protected NotifyingNodeEntry getEntry(SkyKey key) { | |
| return new NotifyingNodeEntry(key); | |
| } | |
| /** Receiver to be informed when an event for a given key occurs. */ | |
| public interface Listener { | |
| @ThreadSafe | |
| void accept(SkyKey key, EventType type, Order order, Object context); | |
| public static Listener NULL_LISTENER = new Listener() { | |
| @Override | |
| public void accept(SkyKey key, EventType type, Order order, Object context) {} | |
| }; | |
| } | |
| /** | |
| * Graph/value entry events that the receiver can be informed of. When writing tests, feel free to | |
| * add additional events here if needed. | |
| */ | |
| public enum EventType { | |
| CREATE_IF_ABSENT, | |
| ADD_REVERSE_DEP, | |
| SIGNAL, | |
| SET_VALUE, | |
| MARK_DIRTY, | |
| IS_CHANGED, | |
| IS_DIRTY | |
| } | |
| public enum Order { | |
| BEFORE, | |
| AFTER | |
| } | |
| protected class NotifyingNodeEntry extends InMemoryNodeEntry { | |
| private final SkyKey myKey; | |
| protected NotifyingNodeEntry(SkyKey key) { | |
| myKey = key; | |
| } | |
| // Note that these methods are not synchronized. Necessary synchronization happens when calling | |
| // the super() methods. | |
| @Override | |
| public DependencyState addReverseDepAndCheckIfDone(SkyKey reverseDep) { | |
| graphListener.accept(myKey, EventType.ADD_REVERSE_DEP, Order.BEFORE, reverseDep); | |
| DependencyState result = super.addReverseDepAndCheckIfDone(reverseDep); | |
| graphListener.accept(myKey, EventType.ADD_REVERSE_DEP, Order.AFTER, reverseDep); | |
| return result; | |
| } | |
| @Override | |
| public boolean signalDep(Version childVersion) { | |
| graphListener.accept(myKey, EventType.SIGNAL, Order.BEFORE, childVersion); | |
| boolean result = super.signalDep(childVersion); | |
| graphListener.accept(myKey, EventType.SIGNAL, Order.AFTER, childVersion); | |
| return result; | |
| } | |
| @Override | |
| public Set<SkyKey> setValue(SkyValue value, Version version) { | |
| graphListener.accept(myKey, EventType.SET_VALUE, Order.BEFORE, value); | |
| Set<SkyKey> result = super.setValue(value, version); | |
| graphListener.accept(myKey, EventType.SET_VALUE, Order.AFTER, value); | |
| return result; | |
| } | |
| @Override | |
| public Pair<? extends Iterable<SkyKey>, ? extends SkyValue> markDirty(boolean isChanged) { | |
| graphListener.accept(myKey, EventType.MARK_DIRTY, Order.BEFORE, isChanged); | |
| Pair<? extends Iterable<SkyKey>, ? extends SkyValue> result = super.markDirty(isChanged); | |
| graphListener.accept(myKey, EventType.MARK_DIRTY, Order.AFTER, isChanged); | |
| return result; | |
| } | |
| @Override | |
| public boolean isChanged() { | |
| graphListener.accept(myKey, EventType.IS_CHANGED, Order.BEFORE, this); | |
| return super.isChanged(); | |
| } | |
| @Override | |
| public boolean isDirty() { | |
| graphListener.accept(myKey, EventType.IS_DIRTY, Order.BEFORE, this); | |
| return super.isDirty(); | |
| } | |
| } | |
| } |