From 952f1fd2d686ea5b6e57275699d5466987655e44 Mon Sep 17 00:00:00 2001
From: unknown <david.antunes-da-silva@irit.fr>
Date: Thu, 21 Jul 2022 15:16:52 +0200
Subject: [PATCH] add FairOrderCycling scheduler + some fixes for
 MonoThreadedCycling and for philosophers example

---
 src/example/philosophes/MainPhilosophe.java   |  46 ++--
 src/example/philosophes/Philosopher.java      |   5 +-
 src/example/philosophes/Waste.java            |   5 +-
 .../schedulers/FairOrderCycling.java          | 225 ++++++++++++++++++
 .../schedulers/FairPosCycling.java            |  65 -----
 .../schedulers/MonoThreadedCycling.java       |   8 +-
 6 files changed, 265 insertions(+), 89 deletions(-)
 create mode 100644 src/mas/implementation/schedulers/FairOrderCycling.java
 delete mode 100644 src/mas/implementation/schedulers/FairPosCycling.java

diff --git a/src/example/philosophes/MainPhilosophe.java b/src/example/philosophes/MainPhilosophe.java
index d228b7a..e3d7b30 100644
--- a/src/example/philosophes/MainPhilosophe.java
+++ b/src/example/philosophes/MainPhilosophe.java
@@ -1,7 +1,7 @@
 package example.philosophes;
 
 import mas.core.Cyclable;
-import mas.implementation.schedulers.MonoThreadedCycling;
+import mas.implementation.schedulers.FairCycling;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -10,7 +10,13 @@ public class MainPhilosophe {
 
     public static void main(String[] args) {
 
-        class MyFairCycling extends MonoThreadedCycling<Cyclable> {
+        int nbCycles = 100;
+        int nAgents = 100;
+
+        /**
+         * An extension of the scheduler to make some performance tests
+         */
+        class MyFairCycling extends FairCycling<Cyclable> {
 
             long startTimeCycle = 0;
 
@@ -22,7 +28,7 @@ public class MainPhilosophe {
 
             @Override
             public boolean stopCondition() {
-                return nbOfCycles == 1000;
+                return nbOfCycles == nbCycles;
             }
 
             @Override
@@ -41,9 +47,6 @@ public class MainPhilosophe {
             }
         }
 
-        int nAgents = 100;
-        int nbCycles = 1000;
-
         Philosopher[] philosophers = new Philosopher[nAgents];
         Fork[] forks = new Fork[nAgents];
 
@@ -60,9 +63,26 @@ public class MainPhilosophe {
             philosophers[i].setRightPhilosopher(philosophers[(i+1) % nAgents]);
         }
 
+        //Sequential execution
+        sequentialPerformanceTest(100, philosophers);
+
+
+        //Amak execution
         MyFairCycling scheduler = new MyFairCycling(philosophers);
         scheduler.setSleep(0);
 
+        final long startTime = System.nanoTime();
+
+        scheduler.start();
+
+        scheduler.waitUntilFinish();
+
+        final long endTime = System.nanoTime();
+        System.out.println("AMAk : Total execution time: " + (endTime / 1000 - startTime / 1000) + " microseconds");
+        System.out.println("\tCycle moyen : " + scheduler.getCycleTime() / 1000 + " microseconds");
+    }
+
+    public static void sequentialPerformanceTest(int nbCycles, Cyclable... cyclables){
         final long startTimeSequential = System.nanoTime();
 
         int nbCycle = 0;
@@ -72,8 +92,8 @@ public class MainPhilosophe {
         while(nbCycle < nbCycles){
             startTimeCycleSequential = System.nanoTime();
 
-            for(Philosopher philosopher : philosophers){
-                philosopher.cycle();
+            for(Cyclable cyclable : cyclables){
+                cyclable.cycle();
             }
 
             cycleTimeSequential.add(System.nanoTime() - startTimeCycleSequential);
@@ -85,15 +105,5 @@ public class MainPhilosophe {
 
         System.out.println("Iterative : Total execution time: " + (endTimeSequential / 1000 - startTimeSequential / 1000) + " microseconds");
         System.out.println("\tCycle : " + cycleTimeSequential.stream().mapToLong(value -> value).average().orElse(0.0) /1000 + " microseconds");
-
-        final long startTime = System.nanoTime();
-
-        scheduler.start();
-
-        scheduler.waitUntilFinish();
-
-        final long endTime = System.nanoTime();
-        System.out.println("AMAk : Total execution time: " + (endTime / 1000 - startTime / 1000) + " microseconds");
-        System.out.println("\tCycle moyen : " + scheduler.getCycleTime() / 1000 + " microseconds");
     }
 }
diff --git a/src/example/philosophes/Philosopher.java b/src/example/philosophes/Philosopher.java
index 555c9ba..0408e7e 100644
--- a/src/example/philosophes/Philosopher.java
+++ b/src/example/philosophes/Philosopher.java
@@ -120,8 +120,11 @@ public class Philosopher extends Agent {
     @Override
     public void act() {
         //System.out.println("Philosopher num " + id + " act");
+        //Add some complexity to the agent
         fibonacciRecursion(23);
-        scheduler.addCyclable(new Waste(id));
+        if(scheduler != null){
+            scheduler.addCyclable(new Waste(id));
+        }
     }
 
     public int fibonacciRecursion(int n){
diff --git a/src/example/philosophes/Waste.java b/src/example/philosophes/Waste.java
index 8d0f84e..8e32e93 100644
--- a/src/example/philosophes/Waste.java
+++ b/src/example/philosophes/Waste.java
@@ -3,6 +3,9 @@ package example.philosophes;
 import mas.core.Cyclable;
 import mas.core.Scheduler;
 
+/**
+ * This class is useful to see how the scheduler adds new cyclables to the system dynamically.
+ */
 public class Waste implements Cyclable {
 
     private int id;
@@ -15,7 +18,7 @@ public class Waste implements Cyclable {
     }
         @Override
     public void cycle() {
-        /*System.out.println("I'm the waste n°" + id);*/
+        //System.out.println("I'm the waste n°" + id);
     }
 
     @Override
diff --git a/src/mas/implementation/schedulers/FairOrderCycling.java b/src/mas/implementation/schedulers/FairOrderCycling.java
new file mode 100644
index 0000000..35708d7
--- /dev/null
+++ b/src/mas/implementation/schedulers/FairOrderCycling.java
@@ -0,0 +1,225 @@
+package mas.implementation.schedulers;
+
+import mas.core.Cyclable;
+import mas.core.Scheduler;
+import mas.core.Sleepable;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * The FairOrderCycling is a one thread scheduler which maintains the same execution order of the cyclables every system's cycle.
+ *
+ * @param <T>
+ *     Extended class of {@link mas.core.Cyclable}
+ *
+ * @author David Antunes
+ */
+public class FairOrderCycling<T extends Cyclable> implements Scheduler<T>, Sleepable {
+
+    /**
+     * The cyclable objects handled by the scheduler.
+     */
+    protected List<T> cyclables = new ArrayList<>();
+
+    /**
+     * The cyclables that must be added in the next cycle.
+     */
+    protected Queue<T> pendingToAddCyclables = new ConcurrentLinkedQueue<>();
+
+    /**
+     * The cyclables that must be removed in the next cycle.
+     */
+    protected Queue<T> pendingToRemoveCyclables = new ConcurrentLinkedQueue<>();
+
+    /**
+     * Time between two cycles. Default time in {@link Sleepable#DEFAULT_SLEEP}.
+     */
+    protected int sleep = DEFAULT_SLEEP;
+
+    /**
+     * Number of system cycles.
+     */
+    protected int nbOfCycles = 0;
+
+    /**
+     * Condition to know if the scheduler must be stopped.
+     */
+    protected boolean mustStop = false;
+
+    /**
+     * Condition to know if the scheduler must be paused.
+     */
+    protected boolean mustPause = false;
+
+    /**
+     * Object used to pause the scheduler.
+     */
+    protected CountDownLatch pauseLatch;
+
+    /**
+     * The thread which executes the cycle.
+     */
+    Thread executionThread = new Thread(this::doCycle);
+
+    /**
+     * Constructor which set the initial cyclables.
+     *
+     * @param cyclables
+     *          The corresponding cyclables
+     */
+    public FairOrderCycling(T... cyclables){
+        for(T cyclable : cyclables){
+            addCyclable(cyclable);
+        }
+    }
+
+    @Override
+    public void start() {
+        executionThread.start();
+    }
+
+    @Override
+    public void stop() {
+        mustStop = true;
+    }
+
+    @Override
+    public void pause() {
+        if(pauseLatch == null || pauseLatch.getCount() == 0){
+            pauseLatch = new CountDownLatch(1);
+        }
+        mustPause = true;
+    }
+
+    @Override
+    public void resume() {
+        if(pauseLatch != null){
+            pauseLatch.countDown();
+        }
+    }
+
+    @Override
+    public void addCyclable(T cyclable) {
+        cyclable.setScheduler(this);
+        pendingToAddCyclables.add(cyclable);
+    }
+
+    @Override
+    public boolean stopCondition() {
+        return false;
+    }
+
+    @Override
+    public boolean isFinished() {
+        return !executionThread.isAlive();
+    }
+
+    @Override
+    public void waitUntilFinish() {
+        try {
+            executionThread.join();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public int getSleep() {
+        return sleep;
+    }
+
+    @Override
+    public void setSleep(int sleep) {
+        this.sleep = sleep;
+    }
+
+    @Override
+    public void doSleep() {
+        if (getSleep() != 0) {
+            try {
+                Thread.sleep(sleep);
+            } catch (final InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * Executes {@link #step()} until the scheduler must stop.
+     */
+    protected void doCycle(){
+        while(!mustStop){
+            step();
+            if(stopCondition()){
+                this.stop();
+            }
+
+            if(mustPause){
+                try{
+                    pauseLatch.await();
+                } catch (InterruptedException e){
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Executes a system's cycle.
+     */
+    protected void step(){
+        onCycleStarts();
+
+        treatPendingCyclables();
+
+        for(int i = 0; i< cyclables.size(); i++){
+            T cyclable = cyclables.get(i);
+            cyclable.cycle();
+            if(cyclable.terminate()){
+                pendingToRemoveCyclables.add(cyclable);
+            }
+        }
+
+        doSleep();
+
+        onCycleEnds();
+
+        nbOfCycles++;
+    }
+
+    /**
+     * Add the cyclables that are going to be scheduled on the current cycle and remove those that are not going to be scheduled.
+     */
+    protected void treatPendingCyclables() {
+        cyclables.removeAll(pendingToRemoveCyclables);
+        cyclables.addAll(pendingToAddCyclables);
+
+        pendingToRemoveCyclables.clear();
+        pendingToAddCyclables.clear();
+    }
+
+    /**
+     * This method is called at the end of every system's cycle.
+     */
+    protected void onCycleEnds() {
+
+    }
+
+    /**
+     * This method is called at the start of every system's cycle.
+     */
+    protected void onCycleStarts(){
+
+    }
+
+    /**
+     * Getter for the number of cycles.
+     *
+     * @return the number of cycles performed by the system
+     */
+    public int getNbOfCycles() {
+        return nbOfCycles;
+    }
+}
diff --git a/src/mas/implementation/schedulers/FairPosCycling.java b/src/mas/implementation/schedulers/FairPosCycling.java
deleted file mode 100644
index c87a5e1..0000000
--- a/src/mas/implementation/schedulers/FairPosCycling.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package mas.implementation.schedulers;
-
-import mas.core.Cyclable;
-import mas.core.Scheduler;
-import mas.core.Sleepable;
-
-/**
- * Même que MonoThreadedCycling + equitable sur position des Round Robin
- */
-public class FairPosCycling implements Scheduler, Sleepable {
-    @Override
-    public void start() {
-
-    }
-
-    @Override
-    public void stop() {
-
-    }
-
-    @Override
-    public void pause() {
-
-    }
-
-    @Override
-    public void resume() {
-
-    }
-
-    @Override
-    public void addCyclable(Cyclable cyclable) {
-
-    }
-
-    @Override
-    public int getSleep() {
-        return 0;
-    }
-
-    @Override
-    public void setSleep(int sleep) {
-
-    }
-
-    @Override
-    public void doSleep() {
-
-    }
-
-    @Override
-    public boolean stopCondition() {
-        return false;
-    }
-
-    @Override
-    public boolean isFinished() {
-        return false;
-    }
-
-    @Override
-    public void waitUntilFinish() {
-
-    }
-}
diff --git a/src/mas/implementation/schedulers/MonoThreadedCycling.java b/src/mas/implementation/schedulers/MonoThreadedCycling.java
index 15af4a5..4957f53 100644
--- a/src/mas/implementation/schedulers/MonoThreadedCycling.java
+++ b/src/mas/implementation/schedulers/MonoThreadedCycling.java
@@ -4,9 +4,7 @@ import mas.core.Cyclable;
 import mas.core.Scheduler;
 import mas.core.Sleepable;
 
-import java.util.LinkedHashSet;
-import java.util.Queue;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.CountDownLatch;
 
@@ -25,7 +23,7 @@ public class MonoThreadedCycling<T extends Cyclable> implements Scheduler<T>, Sl
     /**
      * The cyclable objects handled by the scheduler.
      */
-    protected Set<T> cyclables = new LinkedHashSet<>();
+    protected List<T> cyclables = new ArrayList<>();
 
     /**
      * The cyclables that must be added in the next cycle.
@@ -195,6 +193,8 @@ public class MonoThreadedCycling<T extends Cyclable> implements Scheduler<T>, Sl
      */
     protected void treatPendingCyclables() {
         cyclables.addAll(pendingToAddCyclables);
+        Collections.shuffle(cyclables);
+
         pendingToAddCyclables.clear();
     }
 
-- 
GitLab