From 40d531b4aa2e65e6daea12a25bae8a55967288bc Mon Sep 17 00:00:00 2001 From: shinedday <shinedday@gmail.com> Date: Thu, 20 May 2021 14:21:49 +0200 Subject: [PATCH] Add : scheduler mono threading --- pyAmakCore/classes/scheduler.py | 134 ++++-------------- .../classes/scheduler_mono_threading.py | 99 +++++++++++++ pyAmakCore/classes/scheduler_tool/callable.py | 41 ++++++ pyAmakCore/classes/scheduler_tool/savable.py | 41 ++++++ .../classes/scheduler_tool/scheduler_core.py | 57 ++++++++ pyAmakCore/classes/thread_tool/__init__.py | 0 .../agent_thread.py | 0 .../amas_thread.py | 4 +- .../schedulable_thread.py | 0 pyAmakCore/classes/tools/scheduler_monoIHM.py | 31 ++++ pyAmakCore/tests/memory_leak/main.py | 8 +- 11 files changed, 306 insertions(+), 109 deletions(-) create mode 100644 pyAmakCore/classes/scheduler_mono_threading.py create mode 100644 pyAmakCore/classes/scheduler_tool/callable.py create mode 100644 pyAmakCore/classes/scheduler_tool/savable.py create mode 100644 pyAmakCore/classes/scheduler_tool/scheduler_core.py create mode 100644 pyAmakCore/classes/thread_tool/__init__.py rename pyAmakCore/classes/{scheduler_tool => thread_tool}/agent_thread.py (100%) rename pyAmakCore/classes/{scheduler_tool => thread_tool}/amas_thread.py (95%) rename pyAmakCore/classes/{scheduler_tool => thread_tool}/schedulable_thread.py (100%) create mode 100644 pyAmakCore/classes/tools/scheduler_monoIHM.py diff --git a/pyAmakCore/classes/scheduler.py b/pyAmakCore/classes/scheduler.py index a7d486c..2326a7d 100644 --- a/pyAmakCore/classes/scheduler.py +++ b/pyAmakCore/classes/scheduler.py @@ -5,168 +5,92 @@ from typing import List import sys import pathlib -import pickle -from threading import Semaphore, Thread - -from time import sleep +from threading import Thread sys.path.insert(0, str(pathlib.Path(__file__).parent)) from pyAmakCore.classes.amas import Amas -from pyAmakCore.classes.scheduler_tool.schedulable_thread import SchedulableThread -from pyAmakCore.classes.scheduler_tool.amas_thread import AmasThread +from pyAmakCore.classes.thread_tool.schedulable_thread import SchedulableThread +from pyAmakCore.classes.thread_tool.amas_thread import AmasThread from pyAmakCore.classes.tools.schedulable import Schedulable +from pyAmakCore.classes.scheduler_tool.scheduler_core import SchedulerCore -class Scheduler: +class Scheduler(SchedulerCore): """ Scheduler class, to make sure that environment and amas are always sync together """ def __init__(self, amas: Amas) -> None: - self.amas: Amas = amas - - self.exit_bool: bool = False - - self.semaphore_start_stop: Semaphore = Semaphore(0) + SchedulerCore.__init__(self, amas) - self.schedulables: List[SchedulableThread] = [] - self.schedulables_threads: List[Thread] = [] + self.__schedulables: List[SchedulableThread] = [] + self.__schedulables_threads: List[Thread] = [] - self.add_schedulables(amas, AmasThread) - self.add_schedulables(amas.get_environment(), SchedulableThread) + self.__add_schedulables(amas, AmasThread) + self.__add_schedulables(amas.get_environment(), SchedulableThread) - self.sleep_time: float = 0 - - def get_amas(self) -> Amas: - """ - return amas pointer - """ - return self.amas - - def add_schedulables(self, schedulable: Schedulable, cls) -> None: + def __add_schedulables(self, schedulable: Schedulable, cls) -> None: """ add a schedulable in scheduler """ schedulable_thread = cls(schedulable) - self.schedulables.append(schedulable_thread) + self.__schedulables.append(schedulable_thread) current_thread = Thread(target=schedulable_thread.run) - self.schedulables_threads.append(current_thread) + self.__schedulables_threads.append(current_thread) current_thread.start() - def wait_schedulables(self) -> None: + def __wait_schedulables(self) -> None: """ wait for all schedulable to release a token """ - for schedulable in self.schedulables: + for schedulable in self.__schedulables: schedulable.action_done.acquire() - def start_schedulables(self) -> None: + def __start_schedulables(self) -> None: """ wait for all schedulable to release a token """ - for schedulable in self.schedulables: + for schedulable in self.__schedulables: schedulable.is_waiting.release() def first_part(self) -> None: """ first part of a cycle """ - self.start_schedulables() + self.__start_schedulables() # on cycle begin - self.wait_schedulables() + self.__wait_schedulables() def main_part(self) -> None: """ main part of a cycle """ - self.start_schedulables() + self.__start_schedulables() # agent cycle - self.wait_schedulables() + self.__wait_schedulables() def last_part(self) -> None: """ last part of a cycle """ - self.start_schedulables() + self.__start_schedulables() # on cycle end - self.wait_schedulables() + self.__wait_schedulables() def run(self) -> None: """ - main part of amak core + override super run to close child thread before stopping """ - while not self.exit_bool: - print("Cycle : ", self.amas.get_cycle()) - - self.semaphore_start_stop.acquire() - if self.exit_bool: - break - self.semaphore_start_stop.release() - - self.first_part() - self.main_part() - self.last_part() + super().run() + self.__close_child() - sleep(self.sleep_time) - self.close_child() - - def close_child(self) -> None: + def __close_child(self) -> None: """ tell all child to shut down """ - for schedulable in self.schedulables: + for schedulable in self.__schedulables: schedulable.exit_bool = True schedulable.is_waiting.release() - for thread in self.schedulables_threads: + for thread in self.__schedulables_threads: thread.join(0) - - """ - program interface - """ - - def exit_program(self) -> None: - """ - Exit the system as soon as possible - """ - self.exit_bool = True - self.semaphore_start_stop.release() - - def start(self) -> None: - """ - Unlock the scheduler - """ - self.semaphore_start_stop.release() - - def stop(self) -> None: - """ - Lock the scheduler - """ - self.semaphore_start_stop.acquire() - - def set_sleep(self, sleep_time: int) -> None: - """ - Set sleep value between 2 cycles - """ - self.sleep_time = sleep_time - - """ - load & save program - """ - - def save(self) -> None: - """ - Save the current state of the system - """ - with open('filename.pickle', 'wb') as handle: - pickle.dump(self.amas, handle, protocol=pickle.HIGHEST_PROTOCOL) - - @classmethod - def load(cls) -> 'Scheduler': - """ - Load the last save of the system - """ - with open('filename.pickle', 'rb') as handle: - amas_object = pickle.load(handle) - - return cls(amas_object) diff --git a/pyAmakCore/classes/scheduler_mono_threading.py b/pyAmakCore/classes/scheduler_mono_threading.py new file mode 100644 index 0000000..1f3bbc3 --- /dev/null +++ b/pyAmakCore/classes/scheduler_mono_threading.py @@ -0,0 +1,99 @@ +""" +SchedulerMono class +""" +from random import shuffle +from typing import List + +import sys +import pathlib + + +sys.path.insert(0, str(pathlib.Path(__file__).parent)) + +from pyAmakCore.classes.scheduler_tool.scheduler_core import SchedulerCore +from pyAmakCore.classes.amas import Amas +from pyAmakCore.classes.agent import Agent +from pyAmakCore.classes.tools.schedulable import Schedulable +from pyAmakCore.classes.communicating_agent import CommunicatingAgent +from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy + + +class SchedulerMono(SchedulerCore): + """ + Scheduler class, without threading + """ + + def __init__(self, amas: Amas) -> None: + SchedulerCore.__init__(self, amas) + + self.schedulables: List[Schedulable] = [] + + self.schedulables.append(self.amas) + self.amas.add_pending_agent() + self.schedulables.append(self.amas.get_environment()) + + def first_part(self) -> None: + """ + first part of a cycle + """ + self.amas.add_pending_agent() + + for schedulable in self.schedulables: + schedulable.on_cycle_begin() + + def main_part(self) -> None: + """ + main part of a cycle + """ + + def phase1(current_agent: Agent) -> None: + """ + this is the first phase of a cycle + """ + current_agent.next_phase() + current_agent.on_cycle_begin() + if isinstance(current_agent, CommunicatingAgent): + current_agent.read_mails() + + current_agent.on_perceive() + current_agent.set_criticality(current_agent.compute_criticality()) + current_agent.next_phase() + + def phase2(current_agent: Agent) -> None: + """ + this is the second phase of a cycle + """ + current_agent.next_phase() + current_agent.on_decide() + current_agent.on_act() + current_agent.set_criticality(current_agent.compute_criticality()) + current_agent.next_phase() + current_agent.on_cycle_end() + + agents: List[Agent] = self.amas.get_agents() + + if self.amas.get_execution_policy() == ExecutionPolicy.ONE_PHASE: + shuffle(agents) + for agent in agents: + phase1(agent) + phase2(agent) + else: + shuffle(agents) + for agent in agents: + phase1(agent) + shuffle(agents) + for agent in agents: + phase2(agent) + + def last_part(self) -> None: + """ + last part of a cycle + """ + for schedulable in self.schedulables: + schedulable.on_cycle_end() + + self.amas.remove_pending_agent() + self.amas.to_csv(self.amas.get_cycle(), self.amas.get_agents()) + + for schedulable in self.schedulables: + schedulable.cycle() diff --git a/pyAmakCore/classes/scheduler_tool/callable.py b/pyAmakCore/classes/scheduler_tool/callable.py new file mode 100644 index 0000000..f6f4a51 --- /dev/null +++ b/pyAmakCore/classes/scheduler_tool/callable.py @@ -0,0 +1,41 @@ +""" +class Callable +""" +from threading import Semaphore + + +class Callable: + """ + Class that implement useful method to interact with the program + """ + + def __init__(self) -> None: + self.exit_bool: bool = False + self.sleep_time: float = 0 + + self.semaphore_start_stop: Semaphore = Semaphore(0) + + def exit_program(self) -> None: + """ + Exit the system as soon as possible + """ + self.exit_bool = True + self.semaphore_start_stop.release() + + def start(self) -> None: + """ + Unlock the scheduler + """ + self.semaphore_start_stop.release() + + def stop(self) -> None: + """ + Lock the scheduler + """ + self.semaphore_start_stop.acquire() + + def set_sleep(self, sleep_time: int) -> None: + """ + Set sleep value between 2 cycles + """ + self.sleep_time = sleep_time diff --git a/pyAmakCore/classes/scheduler_tool/savable.py b/pyAmakCore/classes/scheduler_tool/savable.py new file mode 100644 index 0000000..fcffd31 --- /dev/null +++ b/pyAmakCore/classes/scheduler_tool/savable.py @@ -0,0 +1,41 @@ +""" +Savable Class +""" +import pickle +import sys +import pathlib + +sys.path.insert(0, str(pathlib.Path(__file__).parent)) + +from pyAmakCore.classes.amas import Amas + + +class Savable: + """ + Class that implement convenient method to save and load an amas + """ + def __init__(self, amas: Amas) -> None: + self.amas = amas + + def get_amas(self): + """ + return amas + """ + return self.amas + + def save(self) -> None: + """ + Save the current state of the system + """ + with open('filename.pickle', 'wb') as handle: + pickle.dump(self.amas, handle, protocol=pickle.HIGHEST_PROTOCOL) + + @classmethod + def load(cls) -> 'Savable': + """ + Load the last save of the system + """ + with open('filename.pickle', 'rb') as handle: + amas_object = pickle.load(handle) + + return cls(amas_object) \ No newline at end of file diff --git a/pyAmakCore/classes/scheduler_tool/scheduler_core.py b/pyAmakCore/classes/scheduler_tool/scheduler_core.py new file mode 100644 index 0000000..15ef702 --- /dev/null +++ b/pyAmakCore/classes/scheduler_tool/scheduler_core.py @@ -0,0 +1,57 @@ +""" +Scheduler class +""" +from time import sleep + +import sys +import pathlib + +sys.path.insert(0, str(pathlib.Path(__file__).parent)) + +from pyAmakCore.classes.scheduler_tool.callable import Callable +from pyAmakCore.classes.scheduler_tool.savable import Savable +from pyAmakCore.classes.amas import Amas + + +class SchedulerCore(Callable, Savable): + """ + Core part of Scheduler + """ + + def __init__(self, amas: Amas) -> None: + + Callable.__init__(self) + Savable.__init__(self, amas) + + def first_part(self) -> None: + """ + first part of a cycle + """ + + def main_part(self) -> None: + """ + main part of a cycle + """ + + def last_part(self) -> None: + """ + last part of a cycle + """ + + def run(self) -> None: + """ + main part of amak core + """ + while not self.exit_bool: + print("Cycle : ", self.amas.get_cycle()) + + self.semaphore_start_stop.acquire() + if self.exit_bool: + break + self.semaphore_start_stop.release() + + self.first_part() + self.main_part() + self.last_part() + + sleep(self.sleep_time) diff --git a/pyAmakCore/classes/thread_tool/__init__.py b/pyAmakCore/classes/thread_tool/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyAmakCore/classes/scheduler_tool/agent_thread.py b/pyAmakCore/classes/thread_tool/agent_thread.py similarity index 100% rename from pyAmakCore/classes/scheduler_tool/agent_thread.py rename to pyAmakCore/classes/thread_tool/agent_thread.py diff --git a/pyAmakCore/classes/scheduler_tool/amas_thread.py b/pyAmakCore/classes/thread_tool/amas_thread.py similarity index 95% rename from pyAmakCore/classes/scheduler_tool/amas_thread.py rename to pyAmakCore/classes/thread_tool/amas_thread.py index dad5c74..e251638 100644 --- a/pyAmakCore/classes/scheduler_tool/amas_thread.py +++ b/pyAmakCore/classes/thread_tool/amas_thread.py @@ -11,10 +11,10 @@ import pathlib sys.path.insert(0, str(pathlib.Path(__file__).parent.parent)) from pyAmakCore.enumeration.executionPolicy import ExecutionPolicy -from pyAmakCore.classes.scheduler_tool.agent_thread import AgentThread +from pyAmakCore.classes.thread_tool.agent_thread import AgentThread from pyAmakCore.classes.amas import Amas from pyAmakCore.classes.agent import Agent -from pyAmakCore.classes.scheduler_tool.schedulable_thread import SchedulableThread +from pyAmakCore.classes.thread_tool.schedulable_thread import SchedulableThread class AmasThread(SchedulableThread): diff --git a/pyAmakCore/classes/scheduler_tool/schedulable_thread.py b/pyAmakCore/classes/thread_tool/schedulable_thread.py similarity index 100% rename from pyAmakCore/classes/scheduler_tool/schedulable_thread.py rename to pyAmakCore/classes/thread_tool/schedulable_thread.py diff --git a/pyAmakCore/classes/tools/scheduler_monoIHM.py b/pyAmakCore/classes/tools/scheduler_monoIHM.py new file mode 100644 index 0000000..827b998 --- /dev/null +++ b/pyAmakCore/classes/tools/scheduler_monoIHM.py @@ -0,0 +1,31 @@ +""" +Scheduler class that need to be used for pyAmakIhm +""" +import pathlib +import sys + + +sys.path.insert(0, str(pathlib.Path(__file__).parent.parent)) + +from pyAmakCore.classes.amas import Amas +from pyAmakCore.classes.scheduler_mono_threading import SchedulerMono + + +class SchedulerMonoIHM(SchedulerMono): + """ + Convenient class to override while using pyAmakIHM + """ + + def __init__(self, amas: Amas): + self.__observer = None + super().__init__(amas) + + def last_part(self) -> None: + super().last_part() + self.__observer.updateCycle() + + def attach(self, observer: 'Controleur') -> None: + """ + set observer pointer to observer + """ + self.__observer = observer diff --git a/pyAmakCore/tests/memory_leak/main.py b/pyAmakCore/tests/memory_leak/main.py index a1a804f..398af44 100644 --- a/pyAmakCore/tests/memory_leak/main.py +++ b/pyAmakCore/tests/memory_leak/main.py @@ -1,3 +1,5 @@ +from pyAmakCore.classes.scheduler_mono_threading import SchedulerMono + from pyAmakCore.exception.override import ToOverrideWarning from pyAmakCore.classes.agent import Agent from pyAmakCore.classes.amas import Amas @@ -11,7 +13,8 @@ class SimpleAgent(Agent): class SimpleAmas(Amas): def on_initialization(self) -> None: - self.set_do_log(True) + # self.set_do_log(True) + pass def on_initial_agents_creation(self) -> None: for i in range(10): @@ -27,7 +30,8 @@ ToOverrideWarning.enable_warning(False) env = SimpleEnv() amas = SimpleAmas(env) -scheduler = Scheduler(amas) +#scheduler = Scheduler(amas) +scheduler = SchedulerMono(amas) scheduler.start() scheduler.run() -- GitLab