Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Use normal strings instead of f-strings (Sonar).
[simgrid.git] / examples / python / synchro-mutex / synchro-mutex.py
index 5d68323..1a202ba 100644 (file)
@@ -1,3 +1,8 @@
+# Copyright (c) 2010-2023. The SimGrid Team. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the license (GNU LGPL) which comes with this package.
+
 from argparse import ArgumentParser
 from dataclasses import dataclass
 import sys
@@ -14,17 +19,10 @@ def create_parser() -> ArgumentParser:
         help='path to the platform description'
     )
     parser.add_argument(
-        '--workers',
+        '--actors',
         type=int,
         default=6,
-        help='number of workers to start'
-    )
-    parser.add_argument(
-        '--trials-before-success',
-        type=int,
-        default=0,
-        help='number of attempts each workers need to make before getting the correct answer'
-             ' (i.e. number of simulated failures)'
+        help='how many pairs of actors should be started'
     )
     return parser
 
@@ -34,90 +32,42 @@ class ResultHolder:
     value: int
 
 
-class CalculationError(RuntimeError):
-    """ Fake calculation error
-    """
-    pass
-
-
-def worker_context_manager(mutex: Mutex, trials_before_success: int, result: ResultHolder):
-    """ Worker that uses a context manager to acquire/release the shared mutex
-    :param mutex: Shared mutex that guards read/write access to the shared result
-    :param trials_before_success: Number of simulated calculation failures before success
-    :param result: Shared result which will be updated by the worker
-    """
-    this_actor.info(f"I just started")
-    for trial in range(trials_before_success + 1):
-        try:
-            with mutex:
-                this_actor.info(f"acquired the mutex with context manager")
-                this_actor.sleep_for(1)
-                if trial < trials_before_success:
-                    raise CalculationError("did not manage to find the correct answer")
-                result.value += 1
-                this_actor.info(f"updated shared result, which is now {result.value}")
-        except CalculationError as e:
-            this_actor.warning(f"ran in trouble while calculating: {e}. Will retry shortly.")
-        finally:
-            this_actor.info(f"released the mutex after leaving the context manager")
-    this_actor.info("Bye now!")
-
-
-def worker(mutex: Mutex, trials_before_success: int, result: ResultHolder):
-    """ Worker that manually acquires/releases the shared mutex
-    :param mutex: Shared mutex that guards read/write access to the shared result
-    :param trials_before_success: Number of simulated calculation failures before success
-    :param result: Shared result which will be updated by the worker
-    """
-    this_actor.info(f"I just started")
-    for trial in range(trials_before_success + 1):
-        try:
-            mutex.lock()
-            this_actor.info(f"acquired the mutex manually")
-            this_actor.sleep_for(1)
-            if trial < trials_before_success:
-                raise CalculationError("did not manage to find the correct answer")
-            result.value += 1
-            this_actor.info(f"updated shared result, which is now {result.value}")
-        except CalculationError as e:
-            this_actor.warning(f"ran in trouble while calculating: {e}. Will retry shortly.")
-        finally:
-            this_actor.info(f"released the mutex manually")
-            mutex.unlock()
-    this_actor.info("Bye now!")
-
-
-def master(settings):
-    """ Spawns `--workers` workers and wait until they have all updated the shared result, then displays it before
-        leaving. Alternatively spawns `worker_context_manager()` and `worker()` workers.
-    :param settings: Simulation settings
-    """
-    result = ResultHolder(value=0)
-    mutex = Mutex()
-    actors = []
-    for i in range(settings.workers):
-        use_worker_context_manager = i % 2 == 0
-        actors.append(
-            Actor.create(
-                f"worker-{i}(mgr)" if use_worker_context_manager else f"worker-{i}",
-                Host.by_name("Jupiter" if use_worker_context_manager else "Tremblay"),
-                worker_context_manager if use_worker_context_manager else worker,
-                mutex,
-                settings.trials_before_success,
-                result
-            )
-        )
-    [actor.join() for actor in actors]
-    this_actor.info(f"The final result is: {result.value}")
+def worker_context_manager(mutex: Mutex, result: ResultHolder):
+    # When using a context manager, the lock and the unlock are automatic. This is the easiest approach
+    with mutex:
+        this_actor.info("Hello simgrid, I'm ready to compute after acquiring the mutex from a context manager")
+        result.value += 1
+    this_actor.info("I'm done, good bye")
+
+
+def worker(mutex: Mutex, result: ResultHolder):
+    # If you lock your mutex manually, you also have to unlock it.
+    # Beware of exceptions preventing your unlock() from being executed!
+    mutex.lock()
+    this_actor.info("Hello simgrid, I'm ready to compute after a regular lock")
+    result.value += 1
+    mutex.unlock()
+    this_actor.info("I'm done, good bye")
+
 
 
 def main():
     settings = create_parser().parse_known_args()[0]
     e = Engine(sys.argv)
     e.load_platform(settings.platform)
-    Actor.create("master", Host.by_name("Tremblay"), master, settings)
+
+    # Create the requested amount of actors pairs. Each pair has a specific mutex and cell in `result`
+    results = [ResultHolder(value=0) for _ in range(settings.actors)]
+    for i in range(settings.actors):
+        mutex = Mutex()
+        Actor.create(f"worker-{i}(mgr)", Host.by_name("Jupiter"), worker_context_manager, mutex, results[i])
+        Actor.create(f"worker-{i}", Host.by_name("Tremblay"), worker, mutex, results[i])
+
     e.run()
 
+    for i in range(settings.actors):
+        this_actor.info(f"Result[{i}] -> {results[i].value}")
+
 
 if __name__ == "__main__":
     main()