From ebd26ff6ebe13fb28447a4a1d2b347fa26da4366 Mon Sep 17 00:00:00 2001
From: shinedday <shinedday@gmail.com>
Date: Tue, 18 May 2021 16:34:01 +0200
Subject: [PATCH] Rewrite most core

---
 pyAmakCore/classes/agent.py                   |  53 +----
 pyAmakCore/classes/amas.py                    | 147 +++----------
 pyAmakCore/classes/environment.py             |  34 +--
 pyAmakCore/classes/main.py                    |  40 ++++
 pyAmakCore/classes/scheduler.py               | 196 ++++++++++++------
 pyAmakCore/classes/scheduler_tool/__init__.py |   0
 .../classes/scheduler_tool/agent_thread.py    |  65 ++++++
 .../scheduler_tool/schedulable_thread.py      |  43 ++++
 pyAmakCore/classes/{ => tools}/schedulable.py |  38 +---
 pyAmakCore/tests/memory_leak/main.py          |  15 +-
 .../test_get_most_critical_neighbor.py        |   7 +-
 pyAmakCore/tests/test_agent/test_run.py       |  52 -----
 pyAmakCore/tests/test_amas/test_agent.py      |  23 +-
 13 files changed, 347 insertions(+), 366 deletions(-)
 create mode 100644 pyAmakCore/classes/main.py
 create mode 100644 pyAmakCore/classes/scheduler_tool/__init__.py
 create mode 100644 pyAmakCore/classes/scheduler_tool/agent_thread.py
 create mode 100644 pyAmakCore/classes/scheduler_tool/schedulable_thread.py
 rename pyAmakCore/classes/{ => tools}/schedulable.py (52%)
 delete mode 100644 pyAmakCore/tests/test_agent/test_run.py

diff --git a/pyAmakCore/classes/agent.py b/pyAmakCore/classes/agent.py
index f5a979c..70e79b7 100644
--- a/pyAmakCore/classes/agent.py
+++ b/pyAmakCore/classes/agent.py
@@ -7,12 +7,10 @@ from typing import List
 import sys
 import pathlib
 
-
 sys.path.insert(0, str(pathlib.Path(__file__).parent))
 
 from pyAmakCore.exception.override import ToOverrideWarning
 from pyAmakCore.enumeration.agent_phase import Phase
-from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy
 
 
 class Agent:
@@ -116,7 +114,7 @@ class Agent:
         """
         return -inf
 
-    def _get_most_critical_neighbor(self, including_me: bool = False) -> 'Agent':
+    def get_most_critical_neighbor(self, including_me: bool = False) -> 'Agent':
         """
         Convenient method giving the most critical neighbor at a given moment
         """
@@ -135,10 +133,6 @@ class Agent:
 
         return criticalest[randint(0, len(criticalest) - 1)]
 
-    """
-    Cycle
-    """
-
     def on_cycle_begin(self) -> None:
         """
         this method is called by each agent at the start of their cycle
@@ -169,24 +163,7 @@ class Agent:
         """
         ToOverrideWarning("on_act")
 
-    def _phase1(self) -> None:
-        """
-        this is the first phase of a cycle
-        """
-        self.on_perceive()
-        self.__criticality = self.compute_criticality()
-        self._next_phase()
-
-    def _phase2(self) -> None:
-        """
-        this is the second phase of a cycle
-        """
-        self.on_decide()
-        self.on_act()
-        self.__criticality = self.compute_criticality()
-        self._next_phase()
-
-    def _next_phase(self):
+    def next_phase(self):
         next_phase = {
             Phase.INITIALIZING: Phase.PERCEPTION,
             Phase.PERCEPTION: Phase.PERCEPTION_DONE,
@@ -196,32 +173,6 @@ class Agent:
         }
         self.__phase = next_phase.get(self.__phase)
 
-    def run(self) -> None:
-        """
-        Full cycle of an agent
-        """
-        self._next_phase()
-
-        execution_policy = self.__amas.get_execution_policy()
-
-        if execution_policy == ExecutionPolicy.TWO_PHASES:
-            if self.__phase == Phase.PERCEPTION:
-                self.on_cycle_begin()
-                self._phase1()
-                return
-
-            if self.__phase == Phase.DECISION_AND_ACTION:
-                self._phase2()
-                self.on_cycle_end()
-                return
-
-        if execution_policy == ExecutionPolicy.ONE_PHASE:
-            self.on_cycle_begin()
-            self._phase1()
-            self._next_phase()
-            self._phase2()
-            self.on_cycle_end()
-
     def __eq__(self, other: 'Agent') -> bool:
         # check class
         if other is None:
diff --git a/pyAmakCore/classes/amas.py b/pyAmakCore/classes/amas.py
index d24b115..a3e4edc 100644
--- a/pyAmakCore/classes/amas.py
+++ b/pyAmakCore/classes/amas.py
@@ -1,18 +1,15 @@
 """
 Amas Class
 """
-from threading import Thread
 from typing import List
 
 import sys
 import pathlib
 
-
 sys.path.insert(0, str(pathlib.Path(__file__).parent))
 
 from pyAmakCore.classes.tools.loggable import Loggable
-from pyAmakCore.classes.schedulable import Schedulable
-from pyAmakCore.classes.scheduler import Scheduler
+from pyAmakCore.classes.tools.schedulable import Schedulable
 from pyAmakCore.classes.environment import Environment
 from pyAmakCore.classes.agent import Agent
 from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy
@@ -24,43 +21,54 @@ class Amas(Schedulable, Loggable):
     Amas Class
     """
 
-    def __init__(self, environment: Environment) -> None:
+    def __init__(self,
+                 environment: Environment,
+                 execution_policy: ExecutionPolicy = ExecutionPolicy.ONE_PHASE
+                 ) -> None:
 
         Schedulable.__init__(self)
         Loggable.__init__(self)
         self.__environment: Environment = environment
+
         self.__agents: List[Agent] = []
         self.__agent_to_add: List[Agent] = []
         self.__agent_to_remove: List[Agent] = []
 
-        self.__nbrcycle: int = 0
+        self.__execution_policy: ExecutionPolicy = execution_policy
 
-        self.__scheduler: Scheduler = Scheduler()
-        self.__scheduler.add_schedulable(self)
-        self.__environment.add_scheduler(self.__scheduler)
-
-        self.__execution_policy: ExecutionPolicy = ExecutionPolicy.ONE_PHASE
+        self.on_initialization()
 
         self.on_initial_agents_creation()
 
+    def add_pending_agent(self) -> List[Agent]:
+        """
+        add pending agent into agent and return added agents
+        """
         for agent in self.__agent_to_add:
-            if agent not in self.__agents:
-                self.__agents.append(agent)
-        self.__agent_to_add = []
-
-        for agent in self.__agents:
             agent.compute_criticality()
+            self.__agents.append(agent)
+        tmp = self.__agent_to_add
+        self.__agent_to_add = []
+        return tmp
 
-        self.on_initialization()
+    def remove_pending_agent(self):
+        """
+        add pending agent into agent and return removed agents
+        """
+        for agent in self.__agent_to_remove:
+            self.__agents.remove(agent)
+        tmp = self.__agent_to_remove
+        self.__agent_to_remove = []
 
-        # tell scheduler that init is done
-        self.give_token_syncro()
+        return tmp
 
     def add_agent(self, agent: 'Agent') -> None:
         """
         add agent in the amas agents list without duplicate
         """
-        if agent in self.__agents or agent in self.__agent_to_add:
+        if agent in self.__agents:
+            return
+        if agent in self.__agent_to_add:
             return
         self.__agent_to_add.append(agent)
 
@@ -75,6 +83,10 @@ class Amas(Schedulable, Loggable):
         """
         remove agent from amas
         """
+        if agent not in self.__agents:
+            return
+        if agent in self.__agent_to_remove:
+            return
         self.__agent_to_remove.append(agent)
 
     def get_environment(self) -> Environment:
@@ -95,104 +107,9 @@ class Amas(Schedulable, Loggable):
         """
         return self.__execution_policy
 
-    def set_execution_policy(self, execution_policy: ExecutionPolicy) -> None:
-        """
-        set system execution_policy
-        """
-        self.__execution_policy = execution_policy
-
-    def get_cycle(self) -> int:
-        """
-        return nbr of cycle
-        """
-        return self.__nbrcycle
-
     def on_initial_agents_creation(self) -> None:
         """
         This method is called at the end of __init__()
         """
         ToOverrideWarning("on_initial_agents_creation")
 
-    def synchronization(self) -> None:
-        """
-        Unlock Scheduler and wait for his response
-        """
-        self.give_token_syncro()
-        self.__scheduler.take_amas_token()
-
-    def cycle(self) -> None:
-        """
-        Main behavior of Amas
-        """
-        print("Cycle : ", self.__nbrcycle)
-
-        for agent in self.__agent_to_add:
-            if agent not in self.__agents:
-                self.__agents.append(agent)
-        self.__agent_to_add = []
-
-        self.on_cycle_begin()
-        self.synchronization()
-
-        threads = []
-        # suffle
-        for agent in self.__agents:
-            current_thread = Thread(target=agent.run)
-            threads.append(current_thread)
-            current_thread.start()
-
-        for thread in threads:
-            thread.join()
-
-        if self.__execution_policy == ExecutionPolicy.TWO_PHASES:
-            threads = []
-            for agent in self.__agents:
-                current_thread = Thread(target=agent.run)
-                threads.append(current_thread)
-                current_thread.start()
-
-            for thread in threads:
-                thread.join()
-
-        self.synchronization()
-
-        self.on_cycle_end()
-
-        for agent in self.__agent_to_remove:
-            if agent in self.__agents:
-                self.__agents.remove(agent)
-        self.__agent_to_remove = []
-
-        self.to_csv(self.get_cycle(), self.get_agents())
-        self.__nbrcycle += 1
-
-    def put_token(self) -> None:
-        """
-        Tell scheduler to start
-        """
-        self.__scheduler.give_semaphore_start_stop()
-
-    def take_token(self) -> None:
-        """
-        Tell scheduler to stop
-        """
-        self.__scheduler.take_semaphore_start_stop()
-
-    def exit_program(self) -> None:
-        """
-        exit the program at the end of the cycle
-        """
-        self.put_token()
-        self.__scheduler.exit_bool = True
-
-    def set_sleep(self, sleep_time) -> None:
-        """
-        set sleep between 2 cycles
-        """
-        self.__scheduler.sleep_time = sleep_time
-
-    def start(self) -> None:
-        """
-        launch the system
-        """
-        self.__scheduler.run()
diff --git a/pyAmakCore/classes/environment.py b/pyAmakCore/classes/environment.py
index 52c7727..ba45fa2 100644
--- a/pyAmakCore/classes/environment.py
+++ b/pyAmakCore/classes/environment.py
@@ -3,10 +3,10 @@ Environment class
 """
 import sys
 import pathlib
+
 sys.path.insert(0, str(pathlib.Path(__file__).parent))
 
-from pyAmakCore.classes.schedulable import Schedulable
-from pyAmakCore.classes.scheduler import Scheduler
+from pyAmakCore.classes.tools.schedulable import Schedulable
 
 
 class Environment(Schedulable):
@@ -16,34 +16,4 @@ class Environment(Schedulable):
 
     def __init__(self) -> None:
         super().__init__()
-        self.__scheduler: Scheduler = None
         self.on_initialization()
-        # tell scheduler that init is done
-        self.give_token_syncro()
-
-    def add_scheduler(self, scheduler: Scheduler) -> None:
-        """
-        set scheduler pointer to scheduler
-        add add self to schedulables of scheduler
-        """
-        self.__scheduler = scheduler
-        self.__scheduler.add_schedulable(self)
-
-    def synchronization(self) -> None:
-        """
-        Unlock Scheduler and wait for his response
-        """
-        self.give_token_syncro()
-        self.__scheduler.take_environment_token()
-
-    def cycle(self) -> None:
-        """
-        Main behavior of Environment
-        """
-        self.on_cycle_begin()
-        self.synchronization()
-
-        # mileu de cycle
-        self.synchronization()
-
-        self.on_cycle_end()
diff --git a/pyAmakCore/classes/main.py b/pyAmakCore/classes/main.py
new file mode 100644
index 0000000..e6c3dd6
--- /dev/null
+++ b/pyAmakCore/classes/main.py
@@ -0,0 +1,40 @@
+from pyAmakCore.exception.override import ToOverrideWarning
+from pyAmakCore.classes.agent import Agent
+from pyAmakCore.classes.amas import Amas
+from pyAmakCore.classes.environment import Environment
+from pyAmakCore.classes.scheduler import Scheduler
+
+
+class SimpleAgent(Agent):
+    """
+    test
+    """
+
+
+class SimpleAmas(Amas):
+    """
+    test
+    """
+
+    def on_initial_agents_creation(self) -> None:
+        for i in range(10):
+            self.add_agent(SimpleAgent(self))
+
+
+class SimpleEnv(Environment):
+    """
+    test
+    """
+
+import time
+start_time = time.time()
+ToOverrideWarning.enable_warning(False)
+
+env = SimpleEnv()
+amas = SimpleAmas(env)
+
+scheduler = Scheduler(amas)
+
+scheduler.start()
+scheduler.run()
+print("--- %s seconds ---" % (time.time() - start_time))
diff --git a/pyAmakCore/classes/scheduler.py b/pyAmakCore/classes/scheduler.py
index a7c2b59..b362de1 100644
--- a/pyAmakCore/classes/scheduler.py
+++ b/pyAmakCore/classes/scheduler.py
@@ -2,13 +2,19 @@
 Scheduler class
 """
 from threading import Semaphore, Thread
-from time import sleep
 from typing import List
+
+from time import sleep
+
 import sys
 import pathlib
+
 sys.path.insert(0, str(pathlib.Path(__file__).parent))
 
-from pyAmakCore.classes.schedulable import Schedulable
+from pyAmakCore.classes.scheduler_tool.agent_thread import AgentThread
+from pyAmakCore.classes.amas import Amas
+from pyAmakCore.classes.scheduler_tool.schedulable_thread import SchedulableThread
+from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy
 
 
 class Scheduler:
@@ -16,104 +22,156 @@ class Scheduler:
     Scheduler class, to make sure that environment and amas are always sync together
     """
 
-    def __init__(self) -> None:
+    def __init__(self, amas) -> None:
+        self.amas: Amas = amas
 
-        # List of all schedulables
-        self.__schedulables: List[Schedulable] = []
-        # Sleep timer between 2 cycle
-        self.sleep_time: float = 0
+        self.exit_bool = False
 
-        # Semaphore for start/stop button
-        self.__semaphore_start_stop: Semaphore = Semaphore(0)
-        # Semaphore to tell amas to continue
-        self.__semaphore_amas: Semaphore = Semaphore(0)
-        # Semaphore to tell environment to continue
-        self.__semaphore_environment: Semaphore = Semaphore(0)
+        self.semaphore_start_stop = Semaphore(0)
 
-        self.exit_bool = False
+        self.schedulables = []
+        self.schedulables_threads = []
 
-    def add_schedulable(self, schedulable: Schedulable) -> None:
-        """
-        add schedulable in schedulables list
-        """
-        self.__schedulables.append(schedulable)
+        self.add_schedulables(amas)
+        self.add_schedulables(amas.get_environment())
 
-    def take_amas_token(self) -> None:
-        """
-        make amas wait here for a token
-        """
-        self.__semaphore_amas.acquire()
+        self.agents: List[AgentThread] = []
+        self.agents_thread: List[Thread] = []
 
-    def give_amas_token(self) -> None:
-        """
-        unlock amas
-        """
-        self.__semaphore_amas.release()
+        self.sleep_time = 0
 
-    def take_environment_token(self) -> None:
-        """
-        make environment wait here for a token
-        """
-        self.__semaphore_environment.acquire()
+        self.execution_policy = ExecutionPolicy.ONE_PHASE
+
+    def add_schedulables(self, schedulable):
+        schedulable_thread = SchedulableThread(schedulable)
+        self.schedulables.append(schedulable_thread)
+        current_thread = Thread(target=schedulable_thread.run)
+        self.schedulables_threads.append(current_thread)
+        current_thread.start()
+
+    def add_agent(self, agent):
+        agent_thread = AgentThread(agent)
+        self.agents.append(agent_thread)
+        current_thread = Thread(target=agent_thread.run)
+        self.agents_thread.append(current_thread)
+        current_thread.start()
+
+    def add_agents(self):
+        added_agent = self.amas.add_pending_agent()
+        for agent in added_agent:
+            self.add_agent(agent)
+
+    def remove_agents(self):
+        removed_agent = self.amas.remove_pending_agent()
+        for agent in removed_agent:
+            for i in range(len(self.agents)):
+                if agent == self.agents[i].agent:
+                    self.agents[i].exit_bool = True
+                    self.agents[i].is_waiting.release()
+                    self.agents_thread[i].join()
 
-    def give_environment_token(self) -> None:
+                    self.agents.remove(self.agents[i])
+                    self.agents_thread.remove(self.agents_thread[i])
+
+    def wait_schedulables(self) -> None:
         """
-        unlock environment
+        wait for all schedulable to release a token
         """
-        self.__semaphore_environment.release()
+        for i in range(len(self.schedulables)):
+            SchedulableThread.action_done.acquire()
 
-    def take_semaphore_start_stop(self) -> None:
+    def start_schedulables(self) -> None:
         """
-        Scheduler will wait for a token to start the cycle
+        wait for all schedulable to release a token
         """
-        self.__semaphore_start_stop.acquire()
+        for schedulable in self.schedulables:
+            schedulable.is_waiting.release()
 
-    def give_semaphore_start_stop(self) -> None:
+    def wait_agents(self) -> None:
         """
-        give a token to unlock Scheduler
+        wait for all agent to release a token
         """
-        self.__semaphore_start_stop.release()
+        for i in range(len(self.agents)):
+            AgentThread.action_done.acquire()
 
-    def syncro_schedulable(self) -> None:
+    def start_agents(self) -> None:
         """
-        wait for all schedulable to release a token
+        wait for all agent to release a token
         """
-        for schedulable in self.__schedulables:
-            schedulable.take_token_syncro()
+        for agent in self.agents:
+            agent.is_waiting.release()
+
+    def first_part(self):
+        self.add_agents()
+
+        self.amas.to_csv(self.amas.get_cycle(), self.amas.get_agents())
+
+        self.start_schedulables()
+        # on cycle begin
+        self.wait_schedulables()
+
+    def main_part(self):
+        self.start_agents()
+        # all agent run
+
+        if self.execution_policy == ExecutionPolicy.TWO_PHASES:
+            self.wait_agents()
+            # agents are doing phase 2
+            self.start_agents()
+
+        self.wait_agents()
+
+    def last_part(self):
+        self.start_schedulables()
+        # on cycle end
+        self.wait_schedulables()
+
+        self.remove_agents()
 
     def run(self) -> None:
         """
         main part of amak core
         """
-        # wait that all schedulable are ready
-        self.syncro_schedulable()
-
         while not self.exit_bool:
+            print("Cycle : ", self.amas.get_cycle())
 
-            self.__semaphore_start_stop.acquire()
+            self.semaphore_start_stop.acquire()
             if self.exit_bool:
+                self.close_childs()
                 return
-            self.__semaphore_start_stop.release()
+            self.semaphore_start_stop.release()
 
-            threads = []
-            for schedulable in self.__schedulables:
-                current_thread = Thread(target=schedulable.run)
-                threads.append(current_thread)
-                current_thread.start()
+            self.first_part()
+            self.main_part()
+            self.last_part()
 
-            # on cycle begin
-            self.syncro_schedulable()
-            self.give_amas_token()
-            self.give_environment_token()
+            sleep(self.sleep_time)
+        self.close_childs()
 
-            # main part of the cycle
-            self.syncro_schedulable()
-            self.give_amas_token()
-            self.give_environment_token()
+    def close_childs(self):
 
-            # on cycle end
+        for agent in self.agents:
+            agent.exit_bool = True
+            agent.is_waiting.release()
 
-            for thread in threads:
-                thread.join()
+        for thread in self.agents_thread:
+            thread.join(0)
 
-            sleep(self.sleep_time)
+        SchedulableThread.exit_bool = True
+        for schedulable in self.schedulables:
+            schedulable.is_waiting.release()
+        for thread in self.schedulables_threads:
+            thread.join(0)
+
+    def exit_program(self):
+        self.exit_bool = True
+        self.semaphore_start_stop.release()
+
+    def start(self):
+        self.semaphore_start_stop.release()
+
+    def stop(self):
+        self.semaphore_start_stop.acquire()
+
+    def set_sleep(self, sleep_time: int):
+        self.sleep_time = sleep_time
diff --git a/pyAmakCore/classes/scheduler_tool/__init__.py b/pyAmakCore/classes/scheduler_tool/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pyAmakCore/classes/scheduler_tool/agent_thread.py b/pyAmakCore/classes/scheduler_tool/agent_thread.py
new file mode 100644
index 0000000..efaa54e
--- /dev/null
+++ b/pyAmakCore/classes/scheduler_tool/agent_thread.py
@@ -0,0 +1,65 @@
+"""
+Class Agent thread
+"""
+from threading import Semaphore
+import sys
+import pathlib
+
+sys.path.insert(0, str(pathlib.Path(__file__).parent.parent))
+from pyAmakCore.classes.agent import Agent
+from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy
+
+
+class AgentThread:
+    """
+    thread class used to thread agent
+    """
+    action_done = Semaphore(0)
+    execution_policy = ExecutionPolicy.ONE_PHASE
+
+    def __init__(self, agent: Agent):
+
+        self.agent = agent
+        self.is_waiting = Semaphore(0)
+        self.exit_bool = False
+
+    def phase1(self) -> None:
+        """
+        this is the first phase of a cycle
+        """
+        self.agent.on_perceive()
+        self.agent.__criticality = self.agent.compute_criticality()
+        self.agent.next_phase()
+
+    def phase2(self) -> None:
+        """
+        this is the second phase of a cycle
+        """
+        self.agent.on_decide()
+        self.agent.on_act()
+        self.agent.__criticality = self.agent.compute_criticality()
+        self.agent.next_phase()
+
+    def run(self):
+        while not self.exit_bool:
+
+            self.is_waiting.acquire()
+            if self.exit_bool:
+                return
+
+            self.agent.next_phase()
+            self.agent.on_cycle_begin()
+
+            self.phase1()
+
+            if AgentThread.execution_policy == ExecutionPolicy.TWO_PHASES:
+                AgentThread.action_done.release()
+                self.is_waiting.acquire()
+
+            self.agent.next_phase()
+
+            self.phase2()
+
+            self.agent.on_cycle_end()
+
+            AgentThread.action_done.release()
diff --git a/pyAmakCore/classes/scheduler_tool/schedulable_thread.py b/pyAmakCore/classes/scheduler_tool/schedulable_thread.py
new file mode 100644
index 0000000..e24d592
--- /dev/null
+++ b/pyAmakCore/classes/scheduler_tool/schedulable_thread.py
@@ -0,0 +1,43 @@
+"""
+thread class for Schedulable
+"""
+from threading import Semaphore
+
+import sys
+import pathlib
+
+sys.path.insert(0, str(pathlib.Path(__file__).parent.parent))
+
+from pyAmakCore.classes.tools.schedulable import Schedulable
+
+
+class SchedulableThread:
+    """
+    thread class used to thread schedulable
+    """
+    action_done = Semaphore(0)
+    exit_bool = False
+
+    def __init__(self, schedulable: Schedulable):
+
+        self.schedulable = schedulable
+        self.is_waiting = Semaphore(0)
+
+    def run(self):
+        while not SchedulableThread.exit_bool:
+
+            self.is_waiting.acquire()
+            if SchedulableThread.exit_bool:
+                return
+
+            self.schedulable.on_cycle_begin()
+
+            SchedulableThread.action_done.release()
+            # les agents cycles
+            self.is_waiting.acquire()
+
+            self.schedulable.on_cycle_end()
+
+            self.schedulable.cycle()
+
+            SchedulableThread.action_done.release()
diff --git a/pyAmakCore/classes/schedulable.py b/pyAmakCore/classes/tools/schedulable.py
similarity index 52%
rename from pyAmakCore/classes/schedulable.py
rename to pyAmakCore/classes/tools/schedulable.py
index 7cd78bd..c594ba8 100644
--- a/pyAmakCore/classes/schedulable.py
+++ b/pyAmakCore/classes/tools/schedulable.py
@@ -1,10 +1,9 @@
 """
 Schedulable interface
 """
-from threading import Semaphore
 import sys
 import pathlib
-sys.path.insert(0, str(pathlib.Path(__file__).parent))
+sys.path.insert(0, str(pathlib.Path(__file__).parent.parent))
 
 from pyAmakCore.exception.override import ToOverrideWarning
 
@@ -13,7 +12,16 @@ class Schedulable:
     """
     Class Schedulable
     """
-    syncro_semaphore: Semaphore = Semaphore(0)
+
+    def __init__(self):
+
+        self.__nbr_cycle = 0
+
+    def get_cycle(self) -> int:
+        return self.__nbr_cycle
+
+    def cycle(self) -> None:
+        self.__nbr_cycle += 1
 
     def on_initialization(self) -> None:
         """
@@ -32,27 +40,3 @@ class Schedulable:
         This method will be executed at the end of each cycle
         """
         ToOverrideWarning("on_cycle_end")
-
-    def run(self) -> None:
-        """
-        Method that will be called by the thread
-        """
-        self.cycle()
-
-    def cycle(self) -> None:
-        """
-        Main behavior of the Schedulable
-        """
-        ToOverrideWarning("cycle")
-
-    def take_token_syncro(self) -> None:
-        """
-        Scheduler will wait here that the schedulable release a token
-        """
-        self.syncro_semaphore.acquire()
-
-    def give_token_syncro(self) -> None:
-        """
-        Unlock scheduler
-        """
-        self.syncro_semaphore.release()
diff --git a/pyAmakCore/tests/memory_leak/main.py b/pyAmakCore/tests/memory_leak/main.py
index 189fad6..7e0f894 100644
--- a/pyAmakCore/tests/memory_leak/main.py
+++ b/pyAmakCore/tests/memory_leak/main.py
@@ -1,6 +1,8 @@
+from pyAmakCore.exception.override import ToOverrideWarning
+from pyAmakCore.classes.agent import Agent
 from pyAmakCore.classes.amas import Amas
 from pyAmakCore.classes.environment import Environment
-from pyAmakCore.classes.agent import Agent
+from pyAmakCore.classes.scheduler import Scheduler
 
 
 class SimpleAgent(Agent):
@@ -16,7 +18,7 @@ class SimpleAmas(Amas):
 
     def on_initial_agents_creation(self) -> None:
         for i in range(10):
-            self.add_agent(Agent(self))
+            self.add_agent(SimpleAgent(self))
 
 
 class SimpleEnv(Environment):
@@ -24,13 +26,18 @@ class SimpleEnv(Environment):
     test
     """
 
+import time
+start_time = time.time()
+ToOverrideWarning.enable_warning(False)
 
 env = SimpleEnv()
 amas = SimpleAmas(env)
 
-amas.put_token()
+scheduler = Scheduler(amas)
 
-amas.start()
+scheduler.start()
+scheduler.run()
+print("--- %s seconds ---" % (time.time() - start_time))
 
 
 """
diff --git a/pyAmakCore/tests/test_agent/test_get_most_critical_neighbor.py b/pyAmakCore/tests/test_agent/test_get_most_critical_neighbor.py
index 4dff5c4..c190a4a 100644
--- a/pyAmakCore/tests/test_agent/test_get_most_critical_neighbor.py
+++ b/pyAmakCore/tests/test_agent/test_get_most_critical_neighbor.py
@@ -11,9 +11,6 @@ class SimpleAgent(Agent):
     def set_criticality(self, i):
         self._Agent__criticality = i
 
-    def get_most_critical(self, i):
-        return self._get_most_critical_neighbor(i)
-
 
 class TestAgentGetMostCriticalNeighbor(TestCase):
     """
@@ -44,7 +41,7 @@ class TestAgentGetMostCriticalNeighbor(TestCase):
         n4.set_criticality(25)
         agent.add_neighbour(n4)
 
-        self.assertEqual(agent.get_most_critical(False), n4)
+        self.assertEqual(agent.get_most_critical_neighbor(False), n4)
 
     def test_get_most_critical_neighbor_with_self(self):
         Agent.reset_agent()
@@ -70,7 +67,7 @@ class TestAgentGetMostCriticalNeighbor(TestCase):
         n4.set_criticality(25)
         agent.add_neighbour(n4)
 
-        self.assertEqual(agent.get_most_critical(True), agent)
+        self.assertEqual(agent.get_most_critical_neighbor(True), agent)
 
 
 if __name__ == '__main__':
diff --git a/pyAmakCore/tests/test_agent/test_run.py b/pyAmakCore/tests/test_agent/test_run.py
deleted file mode 100644
index 991cb56..0000000
--- a/pyAmakCore/tests/test_agent/test_run.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""
-test that run of Agent work as intended
-"""
-from unittest import TestCase, main
-
-from pyAmakCore.classes.agent import Agent
-from pyAmakCore.classes.amas import Amas
-from pyAmakCore.classes.environment import Environment
-from pyAmakCore.enumeration.agent_phase import Phase
-from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy
-
-
-class TestAgentRun(TestCase):
-    """
-    Test class for Agent run
-    """
-
-    def test_run_one_phase(self):
-        """
-        test that run() work and return correct phase with 1 phase policy
-        """
-        Agent.reset_agent()
-        environment = Environment()
-        amas = Amas(environment)
-        amas.set_execution_policy(ExecutionPolicy.ONE_PHASE)
-
-        agent = Agent(amas)
-        self.assertEqual(agent.get_phase(), Phase.INITIALIZING)
-        agent.run()
-        self.assertEqual(agent.get_phase(), Phase.DECISION_AND_ACTION_DONE)
-        agent.run()
-        self.assertEqual(agent.get_phase(), Phase.DECISION_AND_ACTION_DONE)
-
-    def test_run_two_phase(self):
-        """
-        test that run() work and return correct phase with 2 phase policy
-        """
-        Agent.reset_agent()
-        environment = Environment()
-        amas = Amas(environment)
-        amas.set_execution_policy(ExecutionPolicy.TWO_PHASES)
-
-        agent = Agent(amas)
-        self.assertEqual(agent.get_phase(), Phase.INITIALIZING)
-        agent.run()
-        self.assertEqual(agent.get_phase(), Phase.PERCEPTION_DONE)
-        agent.run()
-        self.assertEqual(agent.get_phase(), Phase.DECISION_AND_ACTION_DONE)
-
-
-if __name__ == '__main__':
-    main()
diff --git a/pyAmakCore/tests/test_amas/test_agent.py b/pyAmakCore/tests/test_amas/test_agent.py
index e43150f..b5a9b18 100644
--- a/pyAmakCore/tests/test_amas/test_agent.py
+++ b/pyAmakCore/tests/test_amas/test_agent.py
@@ -45,24 +45,24 @@ class TestAmasAgents(TestCase):
         agent3 = Agent(amas)
         # add 1 agent
         amas.add_agent(agent1)
-        amas.cycle()
+        amas.add_pending_agent()
         self.assertEqual(amas.get_agents(), [agent1])
 
         # don't remove previous agent
         amas.add_agent(agent2)
-        amas.cycle()
+        amas.add_pending_agent()
         self.assertEqual(amas.get_agents(), [agent1, agent2])
 
         # add agent in good order
         amas.add_agent(agent3)
-        amas.cycle()
+        amas.add_pending_agent()
         self.assertEqual(amas.get_agents(), [agent1, agent2, agent3])
 
         # don't add duplicate
         amas.add_agent(agent1)
         amas.add_agent(agent2)
         amas.add_agent(agent3)
-        amas.cycle()
+        amas.add_pending_agent()
         self.assertEqual(amas.get_agents(), [agent1, agent2, agent3])
 
     def test_add_agents(self) -> None:
@@ -80,16 +80,16 @@ class TestAmasAgents(TestCase):
         agent5 = Agent(amas)
 
         amas.add_agents([agent1, agent2, agent3, agent4, agent5])
-        amas.cycle()
+        amas.add_pending_agent()
         self.assertEqual(amas.get_agents(), [agent1, agent2, agent3, agent4, agent5])
 
         amas = SimplerAmas(environment)
         amas.add_agents([agent1, agent2])
-        amas.cycle()
+        amas.add_pending_agent()
         self.assertEqual(amas.get_agents(), [agent1, agent2])
 
         amas.add_agents([agent1, agent2, agent4, agent2, agent3])
-        amas.cycle()
+        amas.add_pending_agent()
         self.assertEqual(amas.get_agents(), [agent1, agent2, agent4, agent3])
 
     def test_remove_agent(self) -> None:
@@ -101,22 +101,23 @@ class TestAmasAgents(TestCase):
         agent3 = Agent(amas)
 
         amas.remove_agent(agent2)
-        amas.cycle()
+        amas.remove_pending_agent()
         self.assertEqual(amas.get_agents(), [])
 
         amas.add_agents([agent1, agent2, agent3])
+        amas.add_pending_agent()
 
         amas.remove_agent(agent2)
-        amas.cycle()
+        amas.remove_pending_agent()
         self.assertEqual(amas.get_agents(), [agent1, agent3])
 
         amas.remove_agent(agent2)
-        amas.cycle()
+        amas.remove_pending_agent()
         self.assertEqual(amas.get_agents(), [agent1, agent3])
 
         amas.remove_agent(agent1)
         amas.remove_agent(agent3)
-        amas.cycle()
+        amas.remove_pending_agent()
         self.assertEqual(amas.get_agents(), [])
 
 
-- 
GitLab