package deadlock.analyser.detection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import deadlock.analyser.factory.GroupName;
import deadlock.analyser.factory.GroupNameUnique;
//Class State implement the structure that contains a "list of couple (a,b)", this list is implement like an HashMap, the key is a TermVariable and the value is a
//list of other TermVariable which appear in the right side of a couple dependency.
public class State {
//hashmaps storing dependency couples
HashMap<GroupName, HashSet<GroupName>> depCouple;
HashMap<GroupName, HashSet<GroupName>> depCoupleAwait;
DebugInfo di = new DebugInfo();
public DebugInfo getDebugInfo(){
return di;
}
public void setDebugInfo(DebugInfo di){
this.di = di;
}
public State(){
this.depCouple = new HashMap<GroupName, HashSet<GroupName>>();
this.depCoupleAwait = new HashMap<GroupName, HashSet<GroupName>>();
}
//Method for add (a,b) to this State
public void addCouple(GroupName a, GroupName b){
if(!depCouple.containsKey(a))
depCouple.put(a, new HashSet<GroupName>());
HashSet<GroupName> aCouples = depCouple.get(a);
if(!aCouples.contains(b))
aCouples.add(b);
HashSet<GroupName> aCouplesAwait;
if((aCouplesAwait = depCoupleAwait.get(a)) != null && aCouplesAwait.contains(b))
aCouplesAwait.remove(b);
}
//Method for add (a,b)@ to this State
public void addCoupleAwait(GroupName a, GroupName b){
if(!containsCouple(a, b))
{
if(!depCoupleAwait.containsKey(a))
depCoupleAwait.put(a, new HashSet<GroupName>());
HashSet<GroupName> aCouplesAwait = depCoupleAwait.get(a);
if(!aCouplesAwait.contains(b))
aCouplesAwait.add(b);
}
}
//Method for add entire State s to this State, useful for parallel operation
public void addState(State s){
for(GroupName a : s.depCouple.keySet())
for(GroupName b: s.depCouple.get(a))
addCouple(a, b);
for(GroupName a : s.depCoupleAwait.keySet())
for(GroupName b: s.depCoupleAwait.get(a))
addCoupleAwait(a, b);
}
//Method to know if a state is contained in this state
public Boolean containState(State s){
for(GroupName a : s.depCouple.keySet())
for(GroupName b: s.depCouple.get(a))
if(!containsCoupleGet(a, b))
return false;
for(GroupName a : s.depCoupleAwait.keySet())
for(GroupName b: s.depCoupleAwait.get(a))
if(!containsCoupleAwait(a, b))
return false;
return true;
}
//Method to know if a couple get is contained in this state
private boolean containsCoupleGet(GroupName a, GroupName b) {
HashSet<GroupName> aGroup;
return ((aGroup = depCouple.get(a)) != null && aGroup.contains(b)) ;
}
//Method to know if a couple await is contained in this state
private boolean containsCoupleAwait(GroupName a, GroupName b) {
HashSet<GroupName> aGroup;
return ((aGroup = depCoupleAwait.get(a)) != null && aGroup.contains(b)) ;
}
//Method to know if a couple is contained in this state
public Boolean containsCouple(GroupName a, GroupName b){
HashSet<GroupName> aGroup, aGroupAwait;
return ((aGroup = depCouple.get(a)) != null && aGroup.contains(b)) || ((aGroupAwait = depCoupleAwait.get(a)) != null && aGroupAwait.contains(b));
}
//VarSubstitution (renaming) application
public void apply(VarSubstitution s){
depCouple = refreshHashMap(s, depCouple);
depCoupleAwait = refreshHashMap(s, depCoupleAwait);
}
//performs a variable name substitution in dependency couples hashmap, returns new instance with new names
private static HashMap<GroupName, HashSet<GroupName>> refreshHashMap(VarSubstitution s, HashMap<GroupName, HashSet<GroupName>> toRefresh) {
HashMap<GroupName, HashSet<GroupName>> temp = new HashMap<GroupName, HashSet<GroupName>>(toRefresh.size());
for(GroupName a: toRefresh.keySet())
{
GroupName key = (a instanceof GroupNameUnique)? a: s.apply(a);
if(!temp.containsKey(key)){
temp.put(key, new HashSet<GroupName>());
}
for (GroupName b : toRefresh.get(a)){
temp.get(key).add((b instanceof GroupNameUnique)? b: s.apply(b));
}
}
return temp;
}
//Calculates de number of dependencies
public int numberOfDep(){
int i = 0;
for(GroupName v : depCouple.keySet())
i+= depCouple.get(v).size();
for(GroupName v : depCoupleAwait.keySet())
i+= depCoupleAwait.get(v).size();
return i;
}
//calculates all the variables of the state
//TODO ABEL: check correctness
public Set<GroupName> fv(){
Set<GroupName> fv = new TreeSet<GroupName>();
for(GroupName a : depCouple.keySet()){
fv.add(a);
for(GroupName b : depCouple.get(a))
fv.add(b);
}
for(GroupName a : depCoupleAwait.keySet()){
fv.add(a);
for(GroupName b : depCoupleAwait.get(a))
fv.add(b);
}
return fv;
}
//Determines if there is a pure get dependencies cycle (Deadlock)
public boolean hasCycleGet()
{
return hasCycle(depCouple, depCouple);
}
//Determines if there is a pure await dependencies cycle (Livelock)
public boolean hasCycleAwait()
{
return hasCycle(depCouple, depCoupleAwait);
}
//Determines if there is a cycle combining both kind of dependencies (Lock)
public boolean hasCycle()
{
//this doesn't work because there can be a deadlock combining dependencies from both kinds
//return hasCycle = HasCycleGet() || HasCycleAwait();
//this is the correct approach, to create the Union Graph and then check for cycles
HashMap<GroupName, HashSet<GroupName>> allTogether = new HashMap<GroupName, HashSet<GroupName>>();
for(GroupName a : depCouple.keySet())
for(GroupName b: depCouple.get(a)){
if(!allTogether.containsKey(a))
allTogether.put(a, new HashSet<GroupName>());
allTogether.get(a).add(b);
}
for(GroupName a : depCoupleAwait.keySet())
for(GroupName b: depCoupleAwait.get(a)){
if(!allTogether.containsKey(a))
allTogether.put(a, new HashSet<GroupName>());
allTogether.get(a).add(b);
}
return hasCycle(depCouple, allTogether);
}
//Checks for a cycle in a dependency couples hashmap
private static boolean hasCycle(HashMap<GroupName, HashSet<GroupName>> depCouple, HashMap<GroupName, HashSet<GroupName>> graph)
{
HashSet<GroupName> visited = new HashSet<GroupName>();
//HashSet<GroupName> recorded = new HashSet<GroupName>();
ArrayList<GroupName> recorded = new ArrayList<GroupName>();
for(GroupName a : graph.keySet())
{
if(hasCycleUtil(graph, recorded, visited, a))
if(reviewCycle(depCouple, recorded))
return true;
}
return false;
}
private static boolean reviewCycle(HashMap<GroupName, HashSet<GroupName>> depCouple, ArrayList<GroupName> recorded) {
// TODO Auto-generated method stub
for(int i = 0; i < recorded.size(); i++)
{
GroupName a = recorded.get(i);
GroupName b = recorded.get((i + 1) % recorded.size());
if(depCouple.containsKey(a) && depCouple.get(a).contains(b))
return true;
}
return false;
}
//recursive method for cycling detection
private static boolean hasCycleUtil(HashMap<GroupName, HashSet<GroupName>> graph, ArrayList<GroupName> recorded, HashSet<GroupName> visited, GroupName current)
{
//this method performs a classic Breath First Search to check for cycles in an undirected graph and
//keeps tracks of ancestors to determine if the closing edge is in deed a back edge
if(!visited.contains(current))
{
visited.add(current);
recorded.add(current);
if(graph.containsKey(current)){
for(GroupName b : graph.get(current))
{
if(!visited.contains(b) && hasCycleUtil(graph, recorded, visited, b))
return true;
if(recorded.contains(b))
return true;
}
}
recorded.remove(current);
}
return false;
}
//toString method
public String toString(){
String res = "";
for(GroupName v : depCouple.keySet()){
res += v.toString() + ": ";
for(GroupName c : depCouple.get(v)){
res += c.toString() + " ";
}
res += "\n";
}
for(GroupName v : depCoupleAwait.keySet()){
res += v.toString() + ": ";
for(GroupName c : depCoupleAwait.get(v)){
res += c.toString() + " ";
}
res += "\n";
}
return res;
}
// public void expandAndClean() {
// // TODO Auto-generated method stub
// expandAndClean();
//
// //depCoupleAwait = cleanHashMap(depCoupleAwait);
// }
//TODO ABEL: this method can be more efficient
public void expandAndClean(){
HashSet<GroupName> vertexes = new HashSet<GroupName>();
//get all vertex
for(GroupName v : depCouple.keySet())
{
vertexes.add(v);
for(GroupName v2 : depCouple.get(v))
vertexes.add(v2);
}
for(GroupName v : depCoupleAwait.keySet())
{
vertexes.add(v);
for(GroupName v2 : depCoupleAwait.get(v))
vertexes.add(v2);
}
//Floyd Warshall Algorithm
GroupName [] vertexMap = vertexes.toArray(new GroupName[0] );
//initialize graph
int [][] graph = new int[vertexMap.length][];
//get all edges
for (int i = 0; i < vertexMap.length; i++){
graph[i] = new int[vertexMap.length];
for (int j = 0; j < vertexMap.length; j++)
graph[i][j] = (depCouple.containsKey(vertexMap[i]) && depCouple.get(vertexMap[i]).contains(vertexMap[j])) ? 2: ((depCoupleAwait.containsKey(vertexMap[i]) && depCoupleAwait.get(vertexMap[i]).contains(vertexMap[j]))? 1: 0);
}
//calculate transitive closure
for (int k = 0; k < vertexMap.length; k++)
for (int i = 0; i < vertexMap.length; i++)
for (int j = 0; j < vertexMap.length; j++){
graph[i][j] = Math.max(graph[i][j], (graph[i][k] * graph[k][j]));
if(graph[i][j] > 2) graph[i][j] = 2; //normalize to avoid overflow
}
//clean existing
depCouple = new HashMap<GroupName, HashSet<GroupName>>();
depCoupleAwait = new HashMap<GroupName, HashSet<GroupName>>();
//add new couples
for (int i = 0; i < vertexMap.length; i++)
for (int j = 0; j < vertexMap.length; j++)
if(graph[i][j] > 0) {
if((!vertexMap[i].isFresh && !vertexMap[j].isFresh)){
if(graph[i][j] > 1)//is a get couple
this.addCouple(vertexMap[i], vertexMap[j]);
else //is an await couple
this.addCoupleAwait(vertexMap[i], vertexMap[j]);
}
else if (vertexMap[i].isFresh && vertexMap[j].isFresh && i == j){
if(graph[i][j] > 1)//is a get couple
this.addCouple(GroupNameUnique.GetInstance(), GroupNameUnique.GetInstance());
else //is an await couple
this.addCoupleAwait(GroupNameUnique.GetInstance(), GroupNameUnique.GetInstance());
}
}
}
public boolean hasReflexiveState() {
// TODO Auto-generated method stub
for(GroupName a : depCouple.keySet())
if(depCouple.get(a).contains(a))
return true;
return false;
}
}