blob: 58e24974b492e7d378c9ad09f3bd532ce02155c5 [file] [log] [blame]
// 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();
}
}
}