package pt.ul.jarmus;
import java.util.Collection;
import pt.ul.armus.Resource;
import pt.ul.armus.deadlockresolver.DeadlockFoundException;
import pt.ul.armus.edgebuffer.EdgeSet;
import pt.ul.armus.edgebuffer.EdgeSetFactory;
import pt.ul.armus.edgebuffer.TaskHandle;
import com.carrotsearch.hppc.ObjectIntOpenHashMap;
import com.carrotsearch.hppc.ObjectStack;
/**
* Maintains the registered resources.
*
* @author Tiago Cogumbreiro (cogumbreiro@di.fc.ul.pt)
*
*/
public class ResourceManager {
private static final class State {
final ObjectIntOpenHashMap<Object> res;
final TaskHandle handle;
public State(ObjectIntOpenHashMap<Object> res,
TaskHandle handle) {
this.res = res;
this.handle = handle;
}
}
/*
* Obtains the phase of each registered synchronization object. The range is
* the set of registered synchronization objects.
*/
private ObjectIntOpenHashMap<Object> phases = new ObjectIntOpenHashMap<>();
/*
* Holds the state of each task.
*/
private final ObjectStack<State> stack = new ObjectStack<>();
/*
* Used to interact with the deadlock verifier.
*/
private TaskHandle handle;
public ResourceManager(TaskHandle handle) {
assert handle != null;
this.handle = handle;
}
/**
* Begins a new context. This should be called when the thread is executing
* a new task.
*/
public void beginTask(TaskHandle newHandle) {
assert newHandle != null;
// save the state
stack.push(new State(phases, handle));
// create a fresh state
phases = new ObjectIntOpenHashMap<>();
handle = newHandle;
}
/**
* Finish the context. This should be called after the task executes a task.
* @return
*/
public TaskHandle endTask() {
// discard current state and revert to the previous state
TaskHandle oldHandle = handle;
State state = stack.pop();
this.phases = state.res;
this.handle = state.handle;
return oldHandle;
}
/**
* Checks if the synchronization object is being managed.
*
* @param synch
* @return
*/
public boolean isRegistered(Object synch) {
return phases.containsKey(synch);
}
/**
* Track the synchronization object, starting at a given phase.
*
* @param synch
* @param phase
*/
public void register(Object synch, int phase) {
phases.put(synch, phase);
}
/**
* De-registers from a synchronization object.
*
* @param synch
*/
public boolean deregister(Object synch) {
return phases.remove(synch) > 0;
}
/**
* Advances the current phase of the object.
*
* @param synch
* The synchronization object.
* @return The value of the previous phase.
*/
public int advance(Object synch) {
ensureRegistered(synch);
return phases.addTo(synch, 1) - 1;
}
/**
* @param synch
* @throw Throws an exception when the given task is not registered with
* <code>synch</code>.
*/
public void ensureRegistered(Object synch) throws IllegalStateException {
if (!phases.containsKey(synch))
throw new IllegalStateException(
"Synchronization object is not registered: " + synch);
}
/**
* Get the phases on which the task is registered with.
*
* @return
*/
private ResourceVariable[] getRegistered() {
final Object[] keys = phases.keys;
final int[] values = phases.values;
final boolean[] allocated = phases.allocated;
ResourceVariable[] result = new ResourceVariable[phases.size()];
int index = 0;
for (int i = 0; i < allocated.length; i++) {
if (allocated[i]) {
result[index] = new ResourceVariable(keys[i], values[i]);
index++;
}
}
return result;
}
/**
* De-registers from every resource.
*/
public void clear() {
phases.clear();
}
/**
* Generates the restrictions when waiting for a given resource.
*
* @param var
* @return
*/
public void beforeAwaitMany(
Collection<?> synchs, int phase) {
ResourceVariable[] left = new ResourceVariable[synchs.size()];
int index = 0;
for (Object synch : synchs) {
left[index] = new ResourceVariable(synch, phase);
index++;
}
EdgeSet edgeSet = EdgeSetFactory.requestManyAllocateManyArray(left, getRegistered());
setBlocked(edgeSet);
}
/**
* Generates the restrictions when waiting for a given resource.
*
* @param var
* @return
*/
public void beforeAwait(Object synch, int phase) {
ResourceVariable var = new ResourceVariable(synch, phase);
setBlocked(EdgeSetFactory.requestManyAllocateManyArray(new Resource[]{var}, getRegistered()));
}
/**
* Marks the task as blocked.
* @param group
* @throws DeadlockIdentifiedException when a deadlock is found
*/
private void setBlocked(EdgeSet group) throws DeadlockIdentifiedException {
try {
handle.setBlocked(group);
} catch (DeadlockFoundException e) {
// we throw an exception and cancel the invocation of await
if (handle.isBlocked()) {
handle.clearBlocked();
}
throw new DeadlockIdentifiedException(e.getDeadlock());
}
}
/**
* Must be called after the blocking call.
*/
public void afterAwait() {
handle.clearBlocked();
}
/**
* Return the phase number of the given synchronisation object.
* @param synch
* @return
*/
public int getPhase(Object synch) {
return phases.get(synch);
}
}