From 8f22b4f19c17e1c1ef606b1acb12ddb5e08de85f Mon Sep 17 00:00:00 2001
From: tempiaa <tempiaa@eurecom.fr>
Date: Thu, 2 Apr 2020 11:11:25 +0200
Subject: [PATCH] First implementation of liveness check on one state for
 finite-states reachability graphs

---
 .../modelchecker/AvatarModelChecker.java      | 177 +++++++++++++++---
 .../modelchecker/SpecificationLiveness.java   |   6 +-
 .../modelchecker/SpecificationState.java      |   2 +
 .../modelchecker/SpecificationTransition.java |   1 -
 .../ui/window/JDialogAvatarModelChecker.java  |  29 ++-
 5 files changed, 178 insertions(+), 37 deletions(-)

diff --git a/src/main/java/avatartranslator/modelchecker/AvatarModelChecker.java b/src/main/java/avatartranslator/modelchecker/AvatarModelChecker.java
index 67b76599e3..6d2c668cdc 100644
--- a/src/main/java/avatartranslator/modelchecker/AvatarModelChecker.java
+++ b/src/main/java/avatartranslator/modelchecker/AvatarModelChecker.java
@@ -47,6 +47,9 @@ import myutil.TraceManager;
 
 import java.util.*;
 
+import org.apache.commons.io.filefilter.FalseFileFilter;
+
+
 /**
  * Class AvatarModelChecker
  * Avatar Model Checker
@@ -96,6 +99,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
     // Liveness
     private boolean livenessDone;
     private boolean studyLiveness;
+    //private ArrayList<SpecificationLiveness> livenesses;
     private SpecificationLiveness livenessInfo;
     
     //RG limits
@@ -188,9 +192,26 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
         ignoreInternalStates = _b;
     }
 
-    public void setLivenessofState(AvatarStateElement _ase, AvatarBlock _ab) {
-        livenessInfo = new SpecificationLiveness(_ase, _ab);
-        studyLiveness = true;
+    public int setLivenessofState() {
+        int states = 0;
+        for (AvatarBlock block : spec.getListOfBlocks()) {
+            for (AvatarStateMachineElement elt : block.getStateMachine().getListOfElements()) {
+                //TraceManager.addDev("null elt in state machine of block=" + block.getName());
+                if (elt.canBeVerified() && elt.isChecked()) {
+                    livenessInfo = new SpecificationLiveness(elt, block);
+                    states++;
+                    break;
+                }
+            }
+        }
+        if (states > 0) {
+            studyLiveness = true;
+        }
+        return states;
+    }
+    
+    public SpecificationLiveness getLivenessResult() {
+        return livenessInfo;
     }
 
     public int setReachabilityOfSelected() {
@@ -426,7 +447,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
         };
         Timer timer = new Timer("Timer");
 
-        for (i = 0; i < nbOfThreads - 1; i++) {
+        for (i = 0; i < nbOfThreads; i++) {
             ts[i] = new Thread(this);
             ts[i].start();
         }
@@ -479,8 +500,9 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
                 return;
             }
             
-            if (timeLimitReached) {
-                pendingStates.clear();
+            if (timeLimitReached || livenessDone) {
+                emptyPendingStates();
+                notifyAll();
                 return;
             }
 
@@ -677,13 +699,13 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
 
         //TraceManager.addDev("Possible transitions 4:" + transitions.size());
         if (transitions.size() == 0) {
-            if (studyLiveness) {
+            if (studyLiveness && _ss.liveness == false) {
                 livenessInfo.result = false;
-                livenessInfo.state = _ss;
                 livenessDone = true;
-            } else {
-                nbOfDeadlocks++;
+            //} else {
+            //    nbOfDeadlocks++;
             }
+            nbOfDeadlocks++;
             //TraceManager.addDev("Deadlock found");
         }
 
@@ -694,6 +716,16 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
         //   If not a new state, create the link rom the previous state to the new one
         //   Otherwise create the new state and its link, and add it to the pending list of states
         int cptt = 0;
+        
+        boolean livenessStateFound = false;
+        boolean livenessStateCuncurrent = false;
+        
+        if (studyLiveness) {
+            //check for a transition with a false liveness state
+            // true only if there is an unique transition to a state with liveness check
+            livenessStateCuncurrent = checkFalseLivenessFromCurrentState(_ss, transitions);
+        }
+        
         for (SpecificationTransition tr : transitions) {
             //TraceManager.addDev("Handling transitions #" + cptt + " type =" + tr.getType());
             cptt++;
@@ -709,6 +741,11 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
             // doing the synchronization
             executeTransition(_ss, newState, tr);
             String action = tr.infoForGraph;
+            
+            if (studyLiveness) {
+                //set liveness value for state
+                livenessStateFound = setLivenessofState(newState, tr, livenessStateCuncurrent, _ss.liveness);
+            }
 
             // Remove empty transitions if applicable
             if (ignoreEmptyTransitions) {
@@ -729,7 +766,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
             synchronized (this) {
                 similar = states.get(newState.getHash(blockValues));
                 if (similar == null) {
-                    if (!((stateLimitRG && stateID >= stateLimit) || timeLimitReached)) {
+                    if (!((stateLimitRG && stateID >= stateLimit)/* || timeLimitReached*/)) {
                         addState(newState);
                     } else {
                         limitReached = true;
@@ -747,7 +784,10 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
                 //TraceManager.addDev("Putting new state with id = " +  newState.id + " stateID = " + stateID + " states size = " + states.size() + " states by id size = " + statesByID.size());
                 //statesByID.put(newState.id, newState);
             	
-                if ((studyLiveness == false) || (studyLiveness && !(tr.livenessFound))) {
+                if (studyLiveness && livenessStateFound && livenessStateCuncurrent == false) {
+                    livenessDone = true;
+                    livenessInfo.result = false;
+                } else {
                     pendingStates.add(newState);
                 }
 
@@ -763,17 +803,15 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
 
                 // If liveness, must verify that from similar it is possible to go to the considered
                 // state or not.
-
-                if (studyLiveness) {
+                if (studyLiveness && similar.liveness == false) {
+                    if (!newState.liveness) {
+                        //found a loop with false liveness
+                        livenessDone = true;
+                        livenessInfo.result = false;
+                    }
                 }
-
-            }
-            if (studyLiveness && (!tr.livenessFound)) {
-                TraceManager.addDev("Liveness: path without the element found");
-                livenessInfo.result = false;
-                livenessInfo.state = newState;
-                livenessDone = true;
             }
+
             //links.add(link);
             nbOfLinks++;
             _ss.addNext(link);
@@ -799,6 +837,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
     private void computeAllInternalStatesFrom(SpecificationState _ss, SpecificationTransition st) {
         SpecificationState newState = _ss.advancedClone();
         SpecificationState previousState = _ss;
+        boolean livenessStateFound = false;
 
         int cpt = 0;
 
@@ -807,6 +846,12 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
             cpt ++;
             newState.increaseClockOfBlocksExcept(st);
             executeTransition(previousState, newState, st);
+            
+            if (studyLiveness) {
+                //set liveness value for state
+                livenessStateFound = setLivenessofState(newState, st, true, previousState.liveness);
+            }
+            
             if (ignoreEmptyTransitions) {
                 handleNonEmptyUniqueTransition(newState);
             }
@@ -895,7 +940,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
                 synchronized (this) {
                     similar = states.get(newState.getHash(blockValues));
                     if (similar == null) {
-                        if (!(stateLimitRG && stateID >= stateLimit) || timeLimitReached) {
+                        if (!(stateLimitRG && stateID >= stateLimit)/* || timeLimitReached*/) {
                             addState(newState);
                         } else {
                             limitReached = true; //can be removed
@@ -906,8 +951,12 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
                 if (similar != null) {
                 	// check if it has been created by another thread in the meanwhile
                 	link.destinationState = similar;
+                	if (studyLiveness && similar.liveness == false && !livenessStateFound) {
+                        livenessDone = true;
+                        livenessInfo.result = false;
+                	}
                 } else {
-                	link.destinationState = newState;
+                    link.destinationState = newState;
                 	pendingStates.add(newState);
                 }
                 nbOfLinks++;
@@ -1176,13 +1225,13 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
         for (int i = 0; i < _st.transitions.length; i++) {
             ase = getNextState(_st.transitions[i], _newState, 10);
             if (ase != null) {
-
-                if (studyLiveness) {
-                    if (livenessInfo.ref1 == ase) {
-                        TraceManager.addDev("Liveness found on a path");
-                        _st.livenessFound = true;
-                    }
-                }
+                
+//                if (studyLiveness) {
+//                    if (livenessInfo.ref1 == ase) {
+//                        TraceManager.addDev("Liveness found on a path");
+//                        _newState.liveness = true;
+//                    }
+//                }
                 checkElement(ase, _newState);
                 int index = _st.blocks[i].getStateMachine().getIndexOfState(ase);
                 if (index > -1) {
@@ -1215,6 +1264,18 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
         }
         return getNextState(e, _newState, maxNbOfIterations);
     }
+    
+    private AvatarStateElement getNextStateNoCheck(AvatarStateMachineElement e, int maxNbOfIterations) {
+        e = e.getNext(0);
+        if (e instanceof AvatarStateElement) {
+            return (AvatarStateElement) e;
+        }
+        maxNbOfIterations--;
+        if (maxNbOfIterations == 0) {
+            return null;
+        }
+        return getNextStateNoCheck(e, maxNbOfIterations);
+    }
 
     // Execute the actions of a transition, and correspondingly impact the variables of the
     // block executing the transition
@@ -1327,7 +1388,6 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
             SpecificationBlock sb = _ss.blocks[cpt];
             AvatarStateElement ase = asm.allStates[sb.values[SpecificationBlock.STATE_INDEX]];
 
-
             AvatarStateElement aseAfter = getStateWithNonEmptyUniqueTransition(ase, block, sb, _ss);
             if (aseAfter != ase) {
                 //checkElement(aseAfter, _ss);
@@ -1345,6 +1405,12 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
     private AvatarStateElement getStateWithNonEmptyUniqueTransitionArray(AvatarStateElement _ase, AvatarBlock _block, SpecificationBlock _sb, SpecificationState _ss, ArrayList<AvatarStateElement> listOfStates) {
 
         //      TraceManager.addDev("Handling Empty transition of previous=" + _ase.getName());
+        
+        if (studyLiveness) {
+            if (_ase == livenessInfo.ref1) {
+                _ss.liveness = true;
+            }
+        }
 
         if (_ase.getNexts().size() != 1) {
             return _ase;
@@ -1400,6 +1466,55 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
 
 
     }
+    
+    private boolean checkFalseLivenessFromCurrentState(SpecificationState _ss, ArrayList<SpecificationTransition> transitions) {
+        AvatarStateElement ase;
+        boolean livenessState = false;
+        int livenessAlternatives = 0;
+        
+        for (SpecificationTransition tr : transitions) {
+            for (int i = 0; i < tr.transitions.length; i++) {
+                if (tr.transitions[i].getBlock() == livenessInfo.ref2) {
+                    //transition on same state machine to check
+                    ase = getNextStateNoCheck(tr.transitions[i], 10);
+                    if (ase != null) {
+                        livenessAlternatives++;
+                        if (livenessInfo.ref1 == ase) {
+                            TraceManager.addDev("Liveness found on a path");
+                            livenessState = true;
+                        }
+                    }
+                }
+            }
+        }
+        if (livenessAlternatives == 1 && livenessState == true) {
+            //liveness in state is found
+            return true;
+        } else {
+            //liveness state is not found or liveness is not true
+            return false;
+        }
+    }
+    
+    private boolean setLivenessofState(SpecificationState newState, SpecificationTransition tr, boolean livenessStateCuncurrent, boolean precLiveness) {
+        AvatarStateElement ase;
+        boolean found = false;
+        
+        // Find if newState carries the transition to a liveness check state
+        for (int i = 0; i < tr.transitions.length; i++) {
+            ase = getNextStateNoCheck(tr.transitions[i], 10);
+            if (ase != null) {
+                if (livenessInfo.ref1 == ase) {
+                    newState.liveness = livenessStateCuncurrent;
+                    found = true;
+                }
+            }
+        }
+        if (!found) {
+            newState.liveness = precLiveness;
+        }
+        return found;
+    }
 
 
     // Checking elements
diff --git a/src/main/java/avatartranslator/modelchecker/SpecificationLiveness.java b/src/main/java/avatartranslator/modelchecker/SpecificationLiveness.java
index 674a2c0c23..c9a7a13ed3 100644
--- a/src/main/java/avatartranslator/modelchecker/SpecificationLiveness.java
+++ b/src/main/java/avatartranslator/modelchecker/SpecificationLiveness.java
@@ -60,7 +60,7 @@ public class SpecificationLiveness  {
     public SpecificationLiveness(Object _ref1, Object _ref2) {
 	ref1 = _ref1;
 	ref2 = _ref2;
-	result = false;
+	result = true;
 	state = null;
     }
 
@@ -82,9 +82,9 @@ public class SpecificationLiveness  {
 
 	
 	if (result) {
-	    return name + " -> liveness is satisfied"; 
+	    return name + " -> liveness is satisfied\n"; 
 	}
-	return name + " -> liveness is NOT satisfied";
+	return name + " -> liveness is NOT satisfied\n";
 	
     }
 
diff --git a/src/main/java/avatartranslator/modelchecker/SpecificationState.java b/src/main/java/avatartranslator/modelchecker/SpecificationState.java
index 8f5ef83f6b..cbcf1dcddc 100644
--- a/src/main/java/avatartranslator/modelchecker/SpecificationState.java
+++ b/src/main/java/avatartranslator/modelchecker/SpecificationState.java
@@ -64,11 +64,13 @@ public class SpecificationState implements Comparable<SpecificationState>  {
     public boolean hashComputed;
     public long id;
     public LinkedList<SpecificationLink> nexts; // The RG is there
+    public boolean liveness; //trace the liveness check at this state
 
     public ArrayList<SpecificationTransition> transitions;
 
     public SpecificationState() {
         hashComputed = false;
+        liveness = false;
     }
 
     // blocks must not be null
diff --git a/src/main/java/avatartranslator/modelchecker/SpecificationTransition.java b/src/main/java/avatartranslator/modelchecker/SpecificationTransition.java
index 4ff0aa3d29..d375c7f9cf 100644
--- a/src/main/java/avatartranslator/modelchecker/SpecificationTransition.java
+++ b/src/main/java/avatartranslator/modelchecker/SpecificationTransition.java
@@ -64,7 +64,6 @@ public class SpecificationTransition  {
     public AvatarTransition[] transitions;
 
     public String infoForGraph;
-    public boolean livenessFound;
 
 
     public SpecificationTransition() {
diff --git a/src/main/java/ui/window/JDialogAvatarModelChecker.java b/src/main/java/ui/window/JDialogAvatarModelChecker.java
index a3d96a9c11..7ef97e1626 100644
--- a/src/main/java/ui/window/JDialogAvatarModelChecker.java
+++ b/src/main/java/ui/window/JDialogAvatarModelChecker.java
@@ -42,6 +42,7 @@ package ui.window;
 import avatartranslator.AvatarSpecification;
 import avatartranslator.AvatarStateMachineElement;
 import avatartranslator.modelchecker.AvatarModelChecker;
+import avatartranslator.modelchecker.SpecificationLiveness;
 import avatartranslator.modelchecker.SpecificationReachability;
 import avatartranslator.modelchecker.SpecificationReachabilityType;
 import myutil.*;
@@ -179,7 +180,7 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act
 
         //showLiveness = _showLiveness;
         showLiveness = true;
-
+        
         initComponents();
         myInitComponents();
         pack();
@@ -596,6 +597,12 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act
                 jta.append("Computation of Reachability Graph activated\n");
             }
             
+            if (livenessSelected == LIVENESS_SELECTED) {
+                mgui.resetLiveness();
+                res = amc.setLivenessofState();
+                jta.append("Liveness of " + res + " selected elements activated\n");
+            }
+            
             // Limitations
             if (stateLimit.isSelected()) {
             	amc.setStateLimit(true);
@@ -629,7 +636,12 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act
             // Starting model checking
             testGo();
 
-            amc.startModelChecking();
+            if (livenessSelected == LIVENESS_NONE) {
+                amc.startModelChecking();
+            } else {
+                amc.startModelCheckingLiveness();
+            }
+            
             TraceManager.addDev("Model checking done");
             //TraceManager.addDev("RG:" + amc.statesToString() + "\n\n");
 
@@ -661,6 +673,11 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act
                     }
                 }
             }
+            
+            if (livenessSelected != LIVENESS_NONE) {
+                jta.append("\nLiveness Analysis:\n");
+                jta.append(amc.getLivenessResult().toString());
+            }
 
             //TraceManager.addDev(amc.toString());
             //TraceManager.addDev(amc.toString());
@@ -773,6 +790,14 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act
             reachabilitySelected = REACHABILITY_ALL;
         }
         
+        if (noLiveness.isSelected()) {
+            livenessSelected = LIVENESS_NONE;
+        } else if (livenessCheckable.isSelected()) {
+            livenessSelected = LIVENESS_SELECTED;
+        } else {
+            livenessSelected = LIVENESS_ALL;
+        }
+        
         stateLimitField.setEnabled(stateLimit.isSelected());
         limitStatesSelected = stateLimit.isSelected();
         timeLimitField.setEnabled(timeLimit.isSelected());
-- 
GitLab