Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of https://framagit.org/simgrid/simgrid
authormlaurent <mathieu.laurent@ens-rennes.fr>
Mon, 5 Jun 2023 11:59:56 +0000 (13:59 +0200)
committermlaurent <mathieu.laurent@ens-rennes.fr>
Mon, 5 Jun 2023 11:59:56 +0000 (13:59 +0200)
230 files changed:
.github/workflows/ci-batsim.yml
.github/workflows/ci-bigdft.yml
.github/workflows/ci-starpu.yml
.github/workflows/ci-wrench.yml
.github/workflows/docker-stable.yml
.github/workflows/docker.yml
.github/workflows/git.yml
ChangeLog
FindSimGrid.cmake
MANIFEST.in
doc/doxygen/uhood_switch.doc
docs/source/Configuring_SimGrid.rst
docs/source/Plugins.rst
docs/source/Start_your_own_project.rst
docs/source/app_s4u.rst
docs/source/tuto_disk/CMakeLists.txt
docs/source/tuto_network_calibration/CMakeLists.txt
examples/cpp/CMakeLists.txt
examples/cpp/dag-comm/s4u-dag-comm.cpp
examples/cpp/dag-comm/s4u-dag-comm.tesh
examples/cpp/dag-failure/s4u-dag-failure.cpp
examples/cpp/dag-from-dax-simple/s4u-dag-from-dax-simple.cpp
examples/cpp/dag-from-dax-simple/s4u-dag-from-dax-simple.tesh
examples/cpp/dag-from-dot-simple/s4u-dag-from-dot-simple.cpp
examples/cpp/dag-from-dot-simple/s4u-dag-from-dot-simple.tesh
examples/cpp/dag-from-json-simple/s4u-dag-from-json-simple.cpp
examples/cpp/dag-from-json-simple/s4u-dag-from-json-simple.tesh
examples/cpp/dag-io/s4u-dag-io.cpp
examples/cpp/dag-io/s4u-dag-io.tesh
examples/cpp/dag-scheduling/s4u-dag-scheduling.cpp
examples/cpp/dag-simple/s4u-dag-simple.cpp
examples/cpp/dag-simple/s4u-dag-simple.tesh
examples/cpp/dag-tuto/s4u-dag-tuto.cpp
examples/cpp/dag-tuto/s4u-dag-tuto.tesh
examples/cpp/dht-kademlia/message.hpp
examples/cpp/energy-exec-ptask/s4u-energy-exec-ptask.cpp
examples/cpp/exec-dependent/s4u-exec-dependent.cpp
examples/cpp/io-dependent/s4u-io-dependent.cpp
examples/cpp/mc-bugged2/s4u-mc-bugged2.tesh
examples/cpp/network-ns3/s4u-network-ns3-timed.tesh
examples/cpp/operation-simple/s4u-operation-simple.cpp [deleted file]
examples/cpp/operation-simple/s4u-operation-simple.tesh [deleted file]
examples/cpp/operation-switch-host/s4u-operation-switch-host.tesh [deleted file]
examples/cpp/operation-variable-load/s4u-operation-variable-load.tesh [deleted file]
examples/cpp/photovoltaic-simple/s4u-photovoltaic-simple.cpp [new file with mode: 0644]
examples/cpp/photovoltaic-simple/s4u-photovoltaic-simple.tesh [new file with mode: 0644]
examples/cpp/synchro-mutex/s4u-mc-synchro-mutex-stateful.tesh
examples/cpp/synchro-mutex/s4u-mc-synchro-mutex.tesh
examples/cpp/task-io/s4u-task-io.cpp [new file with mode: 0644]
examples/cpp/task-io/s4u-task-io.tesh [new file with mode: 0644]
examples/cpp/task-simple/s4u-task-simple.cpp [new file with mode: 0644]
examples/cpp/task-simple/s4u-task-simple.tesh [new file with mode: 0644]
examples/cpp/task-switch-host/s4u-task-switch-host.cpp [moved from examples/cpp/operation-switch-host/s4u-operation-switch-host.cpp with 58% similarity]
examples/cpp/task-switch-host/s4u-task-switch-host.tesh [new file with mode: 0644]
examples/cpp/task-variable-load/s4u-task-variable-load.cpp [moved from examples/cpp/operation-variable-load/s4u-operation-variable-load.cpp with 51% similarity]
examples/cpp/task-variable-load/s4u-task-variable-load.tesh [new file with mode: 0644]
examples/cpp/trace-process-migration/s4u-trace-process-migration.tesh
examples/platforms/photovoltaic_platform.xml [new file with mode: 0644]
examples/python/CMakeLists.txt
examples/python/task-io/task-io.py [new file with mode: 0644]
examples/python/task-io/task-io.tesh [new file with mode: 0644]
examples/python/task-simple/task-simple.py [new file with mode: 0644]
examples/python/task-simple/task-simple.tesh [new file with mode: 0644]
examples/python/task-switch-host/task-switch-host.py [new file with mode: 0644]
examples/python/task-switch-host/task-switch-host.tesh [new file with mode: 0644]
examples/python/task-variable-load/task-variable-load.py [new file with mode: 0644]
examples/python/task-variable-load/task-variable-load.tesh [new file with mode: 0644]
examples/sthread/pthread-mc-mutex-simple.tesh
examples/sthread/pthread-mc-mutex-simpledeadlock.tesh
examples/sthread/pthread-mc-producer-consumer.tesh
include/simgrid/forward.h
include/simgrid/instr.h
include/simgrid/kernel/ProfileBuilder.hpp
include/simgrid/kernel/Timer.hpp
include/simgrid/kernel/resource/Action.hpp
include/simgrid/kernel/resource/Model.hpp
include/simgrid/kernel/routing/ClusterZone.hpp
include/simgrid/kernel/routing/DijkstraZone.hpp
include/simgrid/kernel/routing/DragonflyZone.hpp
include/simgrid/kernel/routing/EmptyZone.hpp
include/simgrid/kernel/routing/FatTreeZone.hpp
include/simgrid/kernel/routing/FloydZone.hpp
include/simgrid/kernel/routing/FullZone.hpp
include/simgrid/kernel/routing/NetPoint.hpp
include/simgrid/kernel/routing/NetZoneImpl.hpp
include/simgrid/kernel/routing/RoutedZone.hpp
include/simgrid/kernel/routing/StarZone.hpp
include/simgrid/kernel/routing/TorusZone.hpp
include/simgrid/kernel/routing/VivaldiZone.hpp
include/simgrid/kernel/routing/WifiZone.hpp
include/simgrid/plugins/ProducerConsumer.hpp
include/simgrid/plugins/operation.hpp [deleted file]
include/simgrid/plugins/photovoltaic.hpp [new file with mode: 0644]
include/simgrid/plugins/task.hpp [new file with mode: 0644]
include/simgrid/s4u/Activity.hpp
include/simgrid/s4u/Actor.hpp
include/simgrid/s4u/Barrier.hpp
include/simgrid/s4u/Comm.hpp
include/simgrid/s4u/ConditionVariable.hpp
include/simgrid/s4u/Disk.hpp
include/simgrid/s4u/Engine.hpp
include/simgrid/s4u/Exec.hpp
include/simgrid/s4u/Host.hpp
include/simgrid/s4u/Io.hpp
include/simgrid/s4u/Link.hpp
include/simgrid/s4u/Mailbox.hpp
include/simgrid/s4u/Mutex.hpp
include/simgrid/s4u/NetZone.hpp
include/simgrid/s4u/Semaphore.hpp
include/simgrid/s4u/VirtualMachine.hpp
include/simgrid/simix.hpp
include/smpi/forward.hpp
include/xbt/Extendable.hpp
include/xbt/PropertyHolder.hpp
include/xbt/automaton.hpp
include/xbt/backtrace.hpp
include/xbt/config.hpp
include/xbt/file.hpp
include/xbt/functional.hpp
include/xbt/log.hpp
include/xbt/promise.hpp
include/xbt/random.hpp
include/xbt/range.hpp
include/xbt/replay.hpp
include/xbt/signal.hpp
include/xbt/string.hpp
include/xbt/system_error.hpp
include/xbt/utility.hpp
sonar-project.properties
src/bindings/python/simgrid_python.cpp
src/dag/loaders.cpp
src/instr/instr_platform.cpp
src/kernel/activity/ActivityImpl.cpp
src/kernel/activity/CommImpl.cpp
src/kernel/activity/CommImpl.hpp
src/kernel/actor/ActorImpl.cpp
src/kernel/actor/CommObserver.cpp
src/kernel/lmm/bmf.cpp
src/kernel/resource/CpuImpl.cpp
src/kernel/resource/CpuImpl.hpp
src/kernel/resource/DiskImpl.cpp
src/kernel/resource/HostImpl.cpp
src/kernel/resource/StandardLinkImpl.cpp
src/kernel/resource/VirtualMachineImpl.cpp
src/kernel/resource/WifiLinkImpl.cpp
src/kernel/resource/WifiLinkImpl.hpp
src/kernel/resource/models/network_cm02.cpp
src/kernel/resource/models/network_ib.cpp
src/kernel/resource/models/network_ib.hpp
src/kernel/resource/models/network_ns3.cpp
src/kernel/resource/models/network_ns3.hpp
src/kernel/resource/models/ns3/ns3_simulator.cpp
src/kernel/resource/models/ns3/ns3_simulator.hpp
src/kernel/resource/models/ptask_L07.cpp
src/kernel/resource/models/ptask_L07.hpp
src/kernel/routing/NetZoneImpl.cpp
src/kernel/routing/TorusZone.cpp
src/kernel/xml/platf_private.hpp
src/kernel/xml/platf_sax_cb.cpp
src/kernel/xml/sg_platf.cpp
src/mc/api/ActorState.hpp
src/mc/api/ClockVector.cpp [new file with mode: 0644]
src/mc/api/ClockVector.hpp [new file with mode: 0644]
src/mc/api/State.cpp
src/mc/api/State.hpp
src/mc/api/strategy/BasicStrategy.hpp
src/mc/api/strategy/Strategy.hpp
src/mc/explo/CommunicationDeterminismChecker.cpp
src/mc/explo/DFSExplorer.cpp
src/mc/explo/DFSExplorer.hpp
src/mc/explo/Exploration.hpp
src/mc/explo/UdporChecker.hpp
src/mc/explo/odpor/ClockVector_test.cpp [new file with mode: 0644]
src/mc/explo/odpor/Execution.cpp [new file with mode: 0644]
src/mc/explo/odpor/Execution.hpp [new file with mode: 0644]
src/mc/explo/odpor/Execution_test.cpp [new file with mode: 0644]
src/mc/explo/odpor/ReversibleRaceCalculator.cpp [new file with mode: 0644]
src/mc/explo/odpor/ReversibleRaceCalculator.hpp [new file with mode: 0644]
src/mc/explo/odpor/WakeupTree.cpp [new file with mode: 0644]
src/mc/explo/odpor/WakeupTree.hpp [new file with mode: 0644]
src/mc/explo/odpor/WakeupTreeIterator.cpp [new file with mode: 0644]
src/mc/explo/odpor/WakeupTreeIterator.hpp [new file with mode: 0644]
src/mc/explo/odpor/WakeupTree_test.cpp [new file with mode: 0644]
src/mc/explo/odpor/odpor_forward.hpp [new file with mode: 0644]
src/mc/explo/odpor/odpor_tests_private.hpp [new file with mode: 0644]
src/mc/explo/simgrid_mc.cpp
src/mc/explo/udpor/Configuration_test.cpp
src/mc/mc_config.cpp
src/mc/mc_config.hpp
src/mc/remote/RemotePtr.hpp
src/mc/sosp/PageStore_test.cpp
src/mc/sosp/Snapshot_test.cpp
src/mc/transition/TransitionActorJoin.cpp
src/mc/transition/TransitionAny.cpp
src/mc/transition/TransitionComm.cpp
src/mc/transition/TransitionObjectAccess.cpp
src/mc/transition/TransitionRandom.hpp
src/mc/transition/TransitionSynchro.cpp
src/plugins/file_system/s4u_FileSystem.cpp
src/plugins/host_dvfs.cpp
src/plugins/host_energy.cpp
src/plugins/host_load.cpp
src/plugins/link_energy.cpp
src/plugins/link_energy_wifi.cpp
src/plugins/link_load.cpp
src/plugins/operation.cpp [deleted file]
src/plugins/photovoltaic.cpp [new file with mode: 0644]
src/plugins/task.cpp [new file with mode: 0644]
src/plugins/vm/dirty_page_tracking.cpp
src/s4u/s4u_Activity.cpp
src/s4u/s4u_Actor.cpp
src/s4u/s4u_Comm.cpp
src/s4u/s4u_Disk.cpp
src/s4u/s4u_Exec.cpp
src/s4u/s4u_Host.cpp
src/s4u/s4u_Io.cpp
src/s4u/s4u_Link.cpp
src/s4u/s4u_VirtualMachine.cpp
src/smpi/include/smpi_topo.hpp
src/smpi/internals/smpi_replay.cpp
src/smpi/mpi/smpi_f2c.cpp
teshsuite/s4u/comm-fault-scenarios/comm-fault-scenarios.cpp
teshsuite/s4u/dependencies/dependencies.cpp
teshsuite/s4u/storage_client_server/storage_client_server.cpp
teshsuite/s4u/vm-suicide/vm-suicide.cpp
teshsuite/smpi/coll-allreduce-with-leaks/mc-coll-allreduce-with-leaks.tesh
teshsuite/xbt/signals/signals.cpp
tools/address_sanitizer.supp
tools/cmake/DefinePackages.cmake
tools/cmake/Tests.cmake

index 78ac347..f519b1e 100644 (file)
@@ -12,7 +12,7 @@ jobs:
     container: simgrid/unstable
 
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
       - name: Build and test BatSim
         run: |
           set -e
index 581e733..bb44643 100644 (file)
@@ -14,7 +14,7 @@ jobs:
       options: --user 0
 
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
       - name: Build and test BigDFT
         run: |
           set -e
index 6b63238..fbca937 100644 (file)
@@ -12,7 +12,7 @@ jobs:
     container: simgrid/unstable
 
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
       - name: Build and test StarPU
         run: |
           set -e
index 224b03e..1a580b4 100644 (file)
@@ -12,7 +12,7 @@ jobs:
     container: simgrid/unstable
 
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
       - name: Build and test WRENCH
         run: |
           set -e
index 68a4ec3..c0a0ea8 100644 (file)
@@ -24,7 +24,7 @@ jobs:
 
     steps:
       - name: Checkout repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       # Login against a Docker registry except on PR
       # https://github.com/docker/login-action
@@ -65,7 +65,7 @@ jobs:
 
     steps:
       - name: Checkout repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       # Login against a Docker registry except on PR
       # https://github.com/docker/login-action
@@ -104,7 +104,7 @@ jobs:
 
     steps:
       - name: Checkout repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       # Login against a Docker registry except on PR
       # https://github.com/docker/login-action
index 17f6084..5471cea 100644 (file)
@@ -20,7 +20,7 @@ jobs:
 
     steps:
       - name: Checkout repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       # Login against a Docker registry except on PR
       # https://github.com/docker/login-action
@@ -59,7 +59,7 @@ jobs:
 
     steps:
       - name: Checkout repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       # Login against a Docker registry except on PR
       # https://github.com/docker/login-action
index 3bb8225..6a907d5 100644 (file)
@@ -22,7 +22,7 @@ jobs:
 
     steps:
       - name: Checkout repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Init options
         run: |
           echo "CC=${{ matrix.config.cc }}"   >> $GITHUB_ENV
@@ -68,7 +68,7 @@ jobs:
 
     steps:
       - name: Checkout repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       - name: build
         run: |
index d68448e..8e1ae4d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,13 +1,15 @@
 SimGrid (3.32.1) not released yet (target december 22)
 
 General:
+ - SimGrid now requires a compiler with C++17 support for public headers too.
+   Sibling projects should upgrade their FindSimGrid.cmake
  - Remove the MSG API: its EOL was scheduled for 2020.
  - Remove the Java bindings: they were limited to the MSG interface.
  - On Windows, you now need to install WSL2 as the native builds are now disabled.
    It was not really working anyway.
  - Support for 32bits architecture is not tested anymore on our CI infrastructure.
    It may break in the future, but we think that nobody's using SimGrid on 32 bits.
- - Remove the surf module. It was replaced by the kernel/models module, and that 
+ - Remove the surf module. It was replaced by the kernel/models module, and that
    refactoring took almost 10 years to properly complete.
 
 S4U:
@@ -20,17 +22,25 @@ S4U:
    possible.
  - Allow to set a concurrency limit on disks and hosts, as it was already the case for links.
  - Rename Link::get_usage() to Link::get_load() for consistency with Host::
+ - Every signal now come with a static version that is invoked for every object of that class,
+   and an instance version that is invoked for this specific object only. For example, 
+   s4u::Actor::on_suspend_cb() adds a callback that is invoked for the suspend of any actor while
+   s4u::Actor::on_this_suspend_cb() adds a callback for this specific actor only.
+ - Activity::on_suspended_cb() is renamed to Activity::on_suspend_cb(), and fired right before the suspend.
+ - Activity::on_resumed_cb() is renamed to Activity::on_resume_cb(), and fired right before the resume.
+ - Resource::on_state_change_cb() is renamed to Resource::on_onoff_cb() to distinguish from the
+   Activity::on_state_change_cb() that is related to the activity state machine, not on/off.
+ - Activity signals (veto, suspend, resume, completion) are now specialized by activity class.
+   That is, callbacks registered in Exec::on_suspend_cb will not be fired for Comms nor Ios.
+
+New S4U plugins:
+ - Task: They are designed to represent dataflows, i.e, graphs of repeatable Activities.
+   See the examples under examples/cpp/task-* and the documentation in the Plugins page.
+ - Battery: Enable the management of batteries on hosts.
+   See the examples under examples/cpp/battery-* and the documentation in the Plugins page.
+ - Photovoltaic: Enable the management of photovoltaic panels on hosts.
+   See the examples under examples/cpp/photovoltaic-* and the documentation in the Plugins page.
 
-New plugin: Operation
- - Operations are designed to represent workflows, i.e, graphs of repeatable Activities.
- - Documentation: https://simgrid.frama.io/simgrid/Plugins.html#operation
- - Examples: examples/cpp/operation-*
-
-New plugin: Battery
- - Enable the management of batteries on hosts.
- - Documentation: https://simgrid.frama.io/simgrid/Plugins.html#battery
- - Examples: examples/cpp/battery-*
-  
 Kernel:
  - optimize an internal datastructure (use a set instead of a list for ongoing activities),
    leading to a potentially big performance gain, in particular with many detached comms.
@@ -68,8 +78,8 @@ Model checking:
  - Synchronize the MBI tests with upstream.
  - Show the full actor bactraces when replaying a MC trace (with model-check/replay)
    and the status of all actors on deadlocks in MC mode.
- - The safety/stateless aspects of the MC are now enabled by default in all simgrid builds. 
-   Liveness and stateful aspects are still controled by the enabling_model-checking 
+ - The safety/stateless aspects of the MC are now enabled by default in all simgrid builds.
+   Liveness and stateful aspects are still controled by the enabling_model-checking
    configuration option.
  - Stateless model-checking is now usable on any system, including Mac OSX and ARM processors.
 
@@ -670,7 +680,7 @@ The Release release (the French lockdown was eased today).
 
 Important user-visible changes:
  - SimGrid now requires a compiler with C++14 support.
-   Sibling projects should upgrade their FindSimgrid.cmake
+   Sibling projects should upgrade their FindSimGrid.cmake
  - Surf precision default value is now 1e-9, instead of 1e-5. This was changed as
    several users had difficulties to understand issues when using high bandwidth or
    small latency events. The new value was already the default for SMPI and
index 209d709..6f9d9e1 100644 (file)
@@ -37,7 +37,7 @@
 #      (code to use with SimGrid v3.19+)
 #    #endif
 #
-#  Since SimGrid header files require C++14, so we set CMAKE_CXX_STANDARD to 14.
+#  Since SimGrid header files require C++17, so we set CMAKE_CXX_STANDARD to 17.
 #    Change this variable in your own file if you need a later standard.
 
 #
@@ -50,7 +50,7 @@
 
 cmake_minimum_required(VERSION 2.8.12)
 
-set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 find_path(SimGrid_INCLUDE_DIR
@@ -109,26 +109,23 @@ if (SimGrid_FOUND)
     INTERFACE_COMPILE_FEATURES cxx_alias_templates
     IMPORTED_LOCATION ${SimGrid_LIBRARY}
   )
-  # We need C++14, so check for it just in case the user removed it since compiling SimGrid
+  # We need C++17, so check for it just in case the user removed it since compiling SimGrid
   if (NOT CMAKE_VERSION VERSION_LESS 3.8)
-    # 3.8+ allows us to simply require C++14 (or higher)
-    set_property(TARGET SimGrid::SimGrid PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_14)
-  elseif (NOT CMAKE_VERSION VERSION_LESS 3.1)
-    # 3.1+ is similar but for certain features. We pick just one
-    set_property(TARGET SimGrid::SimGrid PROPERTY INTERFACE_COMPILE_FEATURES cxx_attribute_deprecated)
+    # 3.8+ allows us to simply require C++17 (or higher)
+    set_property(TARGET SimGrid::SimGrid PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_17)
   else ()
-    # Old CMake can't do much. Just check the CXX_FLAGS and inform the user when a C++14 feature does not work
+    # Old CMake can't do much. Just check the CXX_FLAGS and inform the user when a C++17 feature does not work
     include(CheckCXXSourceCompiles)
     set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS}")
     check_cxx_source_compiles("
-#if __cplusplus < 201402L
+#if __cplusplus < 201703L
 #error
 #else
 int main(){}
 #endif
-" _SIMGRID_CXX14_ENABLED)
-    if (NOT _SIMGRID_CXX14_ENABLED)
-        message(WARNING "C++14 is required to use SimGrid. Enable it with e.g. -std=c++14")
+" _SIMGRID_CXX17_ENABLED)
+    if (NOT _SIMGRID_CXX17_ENABLED)
+        message(WARNING "C++17 is required to use SimGrid. Enable it with e.g. -std=c++17")
     endif ()
     unset(_SIMGRID_CXX14_ENABLED CACHE)
   endif ()
index 3a5d39c..5ad678c 100644 (file)
@@ -350,12 +350,16 @@ include examples/cpp/network-ns3/s4u-network-ns3-timed.tesh
 include examples/cpp/network-ns3/s4u-network-ns3.cpp
 include examples/cpp/network-wifi/s4u-network-wifi.cpp
 include examples/cpp/network-wifi/s4u-network-wifi.tesh
-include examples/cpp/operation-simple/s4u-operation-simple.cpp
-include examples/cpp/operation-simple/s4u-operation-simple.tesh
-include examples/cpp/operation-switch-host/s4u-operation-switch-host.cpp
-include examples/cpp/operation-switch-host/s4u-operation-switch-host.tesh
-include examples/cpp/operation-variable-load/s4u-operation-variable-load.cpp
-include examples/cpp/operation-variable-load/s4u-operation-variable-load.tesh
+include examples/cpp/task-io/s4u-task-io.cpp
+include examples/cpp/task-io/s4u-task-io.tesh
+include examples/cpp/task-simple/s4u-task-simple.cpp
+include examples/cpp/task-simple/s4u-task-simple.tesh
+include examples/cpp/task-switch-host/s4u-task-switch-host.cpp
+include examples/cpp/task-switch-host/s4u-task-switch-host.tesh
+include examples/cpp/task-variable-load/s4u-task-variable-load.cpp
+include examples/cpp/task-variable-load/s4u-task-variable-load.tesh
+include examples/cpp/photovoltaic-simple/s4u-photovoltaic-simple.cpp
+include examples/cpp/photovoltaic-simple/s4u-photovoltaic-simple.tesh
 include examples/cpp/platform-comm-serialize/s4u-platform-comm-serialize.cpp
 include examples/cpp/platform-comm-serialize/s4u-platform-comm-serialize.tesh
 include examples/cpp/platform-failures/s4u-platform-failures.cpp
@@ -472,6 +476,14 @@ include examples/python/io-degradation/io-degradation.py
 include examples/python/io-degradation/io-degradation.tesh
 include examples/python/network-nonlinear/network-nonlinear.py
 include examples/python/network-nonlinear/network-nonlinear.tesh
+include examples/python/task-io/task-io.py
+include examples/python/task-io/task-io.tesh
+include examples/python/task-simple/task-simple.py
+include examples/python/task-simple/task-simple.tesh
+include examples/python/task-switch-host/task-switch-host.py
+include examples/python/task-switch-host/task-switch-host.tesh
+include examples/python/task-variable-load/task-variable-load.py
+include examples/python/task-variable-load/task-variable-load.tesh
 include examples/python/platform-comm-serialize/platform-comm-serialize.py
 include examples/python/platform-comm-serialize/platform-comm-serialize.tesh
 include examples/python/platform-failures/platform-failures.py
@@ -1832,6 +1844,7 @@ include examples/platforms/onelink.xml
 include examples/platforms/optorsim/gridpp_grid_2004.conf
 include examples/platforms/optorsim/lcg_sept2004_grid.conf
 include examples/platforms/optorsim/transform_optorsim_platform.pl
+include examples/platforms/photovoltaic_platform.xml
 include examples/platforms/profiles/fafard_state.profile
 include examples/platforms/profiles/faulty_host.profile
 include examples/platforms/profiles/ginette_state.profile
@@ -1931,7 +1944,8 @@ include include/simgrid/plugins/file_system.h
 include include/simgrid/plugins/live_migration.h
 include include/simgrid/plugins/load.h
 include include/simgrid/plugins/ns3.hpp
-include include/simgrid/plugins/operation.hpp
+include include/simgrid/plugins/task.hpp
+include include/simgrid/plugins/photovoltaic.hpp
 include include/simgrid/s4u.hpp
 include include/simgrid/s4u/Activity.hpp
 include include/simgrid/s4u/Actor.hpp
@@ -2171,6 +2185,8 @@ include src/mc/AddressSpace.hpp
 include src/mc/VisitedState.cpp
 include src/mc/VisitedState.hpp
 include src/mc/api/ActorState.hpp
+include src/mc/api/ClockVector.cpp
+include src/mc/api/ClockVector.hpp
 include src/mc/api/RemoteApp.cpp
 include src/mc/api/RemoteApp.hpp
 include src/mc/api/State.cpp
@@ -2189,6 +2205,19 @@ include src/mc/explo/LivenessChecker.cpp
 include src/mc/explo/LivenessChecker.hpp
 include src/mc/explo/UdporChecker.cpp
 include src/mc/explo/UdporChecker.hpp
+include src/mc/explo/odpor/ClockVector_test.cpp
+include src/mc/explo/odpor/Execution.cpp
+include src/mc/explo/odpor/Execution.hpp
+include src/mc/explo/odpor/Execution_test.cpp
+include src/mc/explo/odpor/ReversibleRaceCalculator.cpp
+include src/mc/explo/odpor/ReversibleRaceCalculator.hpp
+include src/mc/explo/odpor/WakeupTree.cpp
+include src/mc/explo/odpor/WakeupTree.hpp
+include src/mc/explo/odpor/WakeupTreeIterator.cpp
+include src/mc/explo/odpor/WakeupTreeIterator.hpp
+include src/mc/explo/odpor/WakeupTree_test.cpp
+include src/mc/explo/odpor/odpor_forward.hpp
+include src/mc/explo/odpor/odpor_tests_private.hpp
 include src/mc/explo/simgrid_mc.cpp
 include src/mc/explo/udpor/Comb.hpp
 include src/mc/explo/udpor/Configuration.cpp
@@ -2289,7 +2318,8 @@ include src/plugins/host_load.cpp
 include src/plugins/link_energy.cpp
 include src/plugins/link_energy_wifi.cpp
 include src/plugins/link_load.cpp
-include src/plugins/operation.cpp
+include src/plugins/task.cpp
+include src/plugins/photovoltaic.cpp
 include src/plugins/vm/VmLiveMigration.cpp
 include src/plugins/vm/VmLiveMigration.hpp
 include src/plugins/vm/dirty_page_tracking.cpp
index dc40367..98d55a9 100644 (file)
@@ -411,7 +411,7 @@ type and properly handles exceptions:
 
 @code{cpp}
 template<class F>
-typename std::result_of<F()>::type kernelImmediate(F&& code)
+typename std::result_of_t<F()> kernelImmediate(F&& code)
 {
   // If we are in the simulation kernel, we take the fast path and
   // execute the code directly without simcall
@@ -421,7 +421,7 @@ typename std::result_of<F()>::type kernelImmediate(F&& code)
 
   // If we are in the application, pass the code to the simulation
   // kernel which executes it for us and reports the result:
-  typedef typename std::result_of<F()>::type R;
+  typedef typename std::result_of_t<F()> R;
   simgrid::xbt::Result<R> result;
   simcall_run_kernel([&]{
     xbt_assert(SIMIX_is_maestro(), "Not in maestro");
index 3df5bdd..45a12bc 100644 (file)
@@ -547,13 +547,12 @@ are meant to be detached as well.
 Configuring ns-3
 ^^^^^^^^^^^^^^^^
 
-**Option** ``ns3/TcpModel`` **Default:** "default" (ns-3 default)
+**Option** ``ns3/NetworkModel`` **Default:** "default" (ns-3 default TCP)
 
-When using ns-3, there is an extra item ``ns3/TcpModel``, corresponding
-to the ``ns3::TcpL4Protocol::SocketType`` configuration item in
-ns-3. The only valid values (enforced on the SimGrid side) are
-'default' (no change to the ns-3 configuration), 'NewReno' or 'Reno' or
-'Tahoe'.
+When using ns-3, the item ``ns3/NetworkModel`` can be used to switch between TCP or UDP, and switch the used TCP variante. If
+the item is left unchanged, ns-3 uses the default TCP implementation. With a value of "UDP", ns-3 is set to use UDP instead.
+With the value of either 'NewReno' or 'Cubic', the ``ns3::TcpL4Protocol::SocketType`` configuration item in ns-3 is set to the
+corresponding protocol.
 
 **Option** ``ns3/seed`` **Default:** "" (don't set the seed in ns-3)
 
index 2fae394..aeac133 100644 (file)
@@ -69,48 +69,101 @@ kind of objects, please let us now.
 
 Partial list of existing signals in s4u:
 
-- :cpp:func:`Actor::on_creation <simgrid::s4u::Actor::on_creation_cb>`
+- In actors:
+  :cpp:func:`Actor::on_creation <simgrid::s4u::Actor::on_creation_cb>`
   :cpp:func:`Actor::on_suspend <simgrid::s4u::Actor::on_suspend_cb>`
+  :cpp:func:`Actor::on_this_suspend <simgrid::s4u::Actor::on_this_suspend_cb>`
   :cpp:func:`Actor::on_resume <simgrid::s4u::Actor::on_resume_cb>`
+  :cpp:func:`Actor::on_this_resume <simgrid::s4u::Actor::on_this_resume_cb>`
   :cpp:func:`Actor::on_sleep <simgrid::s4u::Actor::on_sleep_cb>`
+  :cpp:func:`Actor::on_this_sleep <simgrid::s4u::Actor::on_this_sleep_cb>`
   :cpp:func:`Actor::on_wake_up <simgrid::s4u::Actor::on_wake_up_cb>`
+  :cpp:func:`Actor::on_this_wake_up <simgrid::s4u::Actor::on_this_wake_up_cb>`
   :cpp:func:`Actor::on_host_change <simgrid::s4u::Actor::on_host_change_cb>`
+  :cpp:func:`Actor::on_this_host_change <simgrid::s4u::Actor::on_this_host_change_cb>`
   :cpp:func:`Actor::on_termination <simgrid::s4u::Actor::on_termination_cb>`
+  :cpp:func:`Actor::on_this_termination <simgrid::s4u::Actor::on_this_termination_cb>`
   :cpp:func:`Actor::on_destruction <simgrid::s4u::Actor::on_destruction_cb>`
-- :cpp:func:`Comm::on_send <simgrid::s4u::Comm::on_send_cb>`
-  :cpp:func:`Comm::on_recv <simgrid::s4u::Comm::on_recv_cb>`
-  :cpp:func:`Comm::on_completion <simgrid::s4u::Comm::on_completion_cb>`
-- :cpp:func:`CommImpl::on_start <simgrid::s4u::Comm::on_start_cb>`
-  :cpp:func:`CommImpl::on_completion <simgrid::s4u::Comm::on_completion_cb>`
-- :cpp:func:`Disk::on_creation <simgrid::s4u::Disk::on_creation_cb>`
-  :cpp:func:`Disk::on_destruction <simgrid::s4u::Disk::on_destruction_cb>`
-  :cpp:func:`Disk::on_state_change <simgrid::s4u::Disk::on_state_change_cb>`
-- :cpp:func:`Engine::on_platform_creation <simgrid::s4u::Engine::on_platform_creation_cb>`
+- In the engine:
+  :cpp:func:`Engine::on_platform_creation <simgrid::s4u::Engine::on_platform_creation_cb>`
   :cpp:func:`Engine::on_platform_created <simgrid::s4u::Engine::on_platform_created_cb>`
   :cpp:func:`Engine::on_time_advance <simgrid::s4u::Engine::on_time_advance_cb>`
   :cpp:func:`Engine::on_simulation_end <simgrid::s4u::Engine::on_simulation_end_cb>`
   :cpp:func:`Engine::on_deadlock <simgrid::s4u::Engine::on_deadlock_cb>`
-- :cpp:func:`Exec::on_start <simgrid::s4u::Exec::on_start_cb>`
-  :cpp:func:`Exec::on_completion <simgrid::s4u::Exec::on_completion_cb>`
-- :cpp:func:`Host::on_creation <simgrid::s4u::Host::on_creation_cb>`
-  :cpp:func:`Host::on_destruction <simgrid::s4u::Host::on_destruction_cb>`
-  :cpp:func:`Host::on_state_change <simgrid::s4u::Host::on_state_change_cb>`
-  :cpp:func:`Host::on_speed_change <simgrid::s4u::Host::on_speed_change_cb>`
-- :cpp:func:`Io::on_start <simgrid::s4u::Io::on_start_cb>`
-  :cpp:func:`Io::on_completion <simgrid::s4u::Io::on_completion_cb>`
-- :cpp:func:`Link::on_creation <simgrid::s4u::Link::on_creation_cb>`
-  :cpp:func:`Link::on_destruction <simgrid::s4u::Link::on_destruction_cb>`
-  :cpp:func:`Link::on_state_change <simgrid::s4u::Link::on_state_change_cb>`
-  :cpp:func:`Link::on_speed_change <simgrid::s4u::Link::on_bandwidth_change_cb>`
-  :cpp:func:`Link::on_communication_state_change <simgrid::s4u::Link::on_communication_state_change_cb>`
-- :cpp:func:`NetZone::on_creation <simgrid::s4u::NetZone::on_creation_cb>`
-  :cpp:func:`NetZone::on_seal <simgrid::s4u::NetZone::on_seal_cb>`
-- :cpp:func:`VirtualMachine::on_start <simgrid::s4u::VirtualMachine::on_start_cb>`
-  :cpp:func:`VirtualMachine::on_started <simgrid::s4u::VirtualMachine::on_started_cb>`
-  :cpp:func:`VirtualMachine::on_suspend <simgrid::s4u::VirtualMachine::on_suspend_cb>`
-  :cpp:func:`VirtualMachine::on_resume <simgrid::s4u::VirtualMachine::on_resume_cb>`
-  :cpp:func:`VirtualMachine::on_migration_start <simgrid::s4u::VirtualMachine::on_migration_start_cb>`
-  :cpp:func:`VirtualMachine::on_migration_end <simgrid::s4u::VirtualMachine::on_migration_end_cb>`
+
+- In resources:
+
+  - :cpp:func:`Disk::on_creation <simgrid::s4u::Disk::on_creation_cb>`
+    :cpp:func:`Disk::on_destruction <simgrid::s4u::Disk::on_destruction_cb>`
+    :cpp:func:`Disk::on_this_destruction <simgrid::s4u::Disk::on_this_destruction_cb>`
+    :cpp:func:`Disk::on_onoff <simgrid::s4u::Disk::on_onoff_cb>`
+    :cpp:func:`Disk::on_this_onoff <simgrid::s4u::Disk::on_this_onoff_cb>`
+  - :cpp:func:`Host::on_creation <simgrid::s4u::Host::on_creation_cb>`
+    :cpp:func:`Host::on_destruction <simgrid::s4u::Host::on_destruction_cb>`
+    :cpp:func:`Host::on_this_destruction <simgrid::s4u::Host::on_this_destruction_cb>`
+    :cpp:func:`Host::on_onoff <simgrid::s4u::Host::on_onoff_cb>`
+    :cpp:func:`Host::on_this_onoff <simgrid::s4u::Host::on_this_onoff_cb>`
+    :cpp:func:`Host::on_speed_change <simgrid::s4u::Host::on_speed_change_cb>`
+    :cpp:func:`Host::on_this_speed_change <simgrid::s4u::Host::on_this_speed_change_cb>`
+    :cpp:func:`Host::on_exec_state_change <simgrid::s4u::Host::on_exec_state_change_cb>`
+  - :cpp:func:`Link::on_creation <simgrid::s4u::Link::on_creation_cb>`
+    :cpp:func:`Link::on_destruction <simgrid::s4u::Link::on_destruction_cb>`
+    :cpp:func:`Link::on_this_destruction <simgrid::s4u::Link::on_this_destruction_cb>`
+    :cpp:func:`Link::on_onoff <simgrid::s4u::Link::on_onoff_cb>`
+    :cpp:func:`Link::on_this_onoff <simgrid::s4u::Link::on_this_onoff_cb>`
+    :cpp:func:`Link::on_bandwidth_change <simgrid::s4u::Link::on_bandwidth_change_cb>`
+    :cpp:func:`Link::on_this_bandwidth_change <simgrid::s4u::Link::on_this_bandwidth_change_cb>`
+    :cpp:func:`Link::on_communication_state_change <simgrid::s4u::Link::on_communication_state_change_cb>`
+
+  - :cpp:func:`NetZone::on_creation <simgrid::s4u::NetZone::on_creation_cb>`
+    :cpp:func:`NetZone::on_seal <simgrid::s4u::NetZone::on_seal_cb>`
+  - :cpp:func:`VirtualMachine::on_start <simgrid::s4u::VirtualMachine::on_start_cb>`
+    :cpp:func:`VirtualMachine::on_this_start <simgrid::s4u::VirtualMachine::on_this_start_cb>`
+    :cpp:func:`VirtualMachine::on_started <simgrid::s4u::VirtualMachine::on_started_cb>`
+    :cpp:func:`VirtualMachine::on_this_started <simgrid::s4u::VirtualMachine::on_this_started_cb>`
+    :cpp:func:`VirtualMachine::on_suspend <simgrid::s4u::VirtualMachine::on_suspend_cb>`
+    :cpp:func:`VirtualMachine::on_this_suspend <simgrid::s4u::VirtualMachine::on_this_suspend_cb>`
+    :cpp:func:`VirtualMachine::on_resume <simgrid::s4u::VirtualMachine::on_resume_cb>`
+    :cpp:func:`VirtualMachine::on_this_resume <simgrid::s4u::VirtualMachine::on_this_resume_cb>`
+    :cpp:func:`VirtualMachine::on_migration_start <simgrid::s4u::VirtualMachine::on_migration_start_cb>`
+    :cpp:func:`VirtualMachine::on_this_migration_start <simgrid::s4u::VirtualMachine::on_this_migration_start_cb>`
+    :cpp:func:`VirtualMachine::on_migration_end <simgrid::s4u::VirtualMachine::on_migration_end_cb>`
+    :cpp:func:`VirtualMachine::on_this_migration_end <simgrid::s4u::VirtualMachine::on_this_migration_end_cb>`
+
+- In activities:
+
+  - :cpp:func:`Comm::on_send <simgrid::s4u::Comm::on_send_cb>`
+    :cpp:func:`Comm::on_recv <simgrid::s4u::Comm::on_recv_cb>`
+  - :cpp:func:`Comm::on_start <simgrid::s4u::Comm::on_start_cb>`
+    :cpp:func:`Comm::on_this_start <simgrid::s4u::Comm::on_this_start_cb>`
+    :cpp:func:`Comm::on_completion <simgrid::s4u::Comm::on_completion_cb>`
+    :cpp:func:`Comm::on_this_completion <simgrid::s4u::Comm::on_this_completion_cb>`
+    :cpp:func:`Comm::on_suspend <simgrid::s4u::Comm::on_suspend_cb>`
+    :cpp:func:`Comm::on_this_suspend <simgrid::s4u::Comm::on_this_suspend_cb>`
+    :cpp:func:`Comm::on_resume <simgrid::s4u::Comm::on_resume_cb>`
+    :cpp:func:`Comm::on_this_resume <simgrid::s4u::Comm::on_this_resume_cb>`
+    :cpp:func:`Comm::on_veto <simgrid::s4u::Comm::on_veto_cb>`
+    :cpp:func:`Comm::on_this_veto <simgrid::s4u::Comm::on_this_veto_cb>`
+  - :cpp:func:`Exec::on_start <simgrid::s4u::Exec::on_start_cb>`
+    :cpp:func:`Exec::on_this_start <simgrid::s4u::Exec::on_this_start_cb>`
+    :cpp:func:`Exec::on_completion <simgrid::s4u::Exec::on_completion_cb>`
+    :cpp:func:`Exec::on_this_completion <simgrid::s4u::Exec::on_this_completion_cb>`
+    :cpp:func:`Exec::on_suspend <simgrid::s4u::Exec::on_suspend_cb>`
+    :cpp:func:`Exec::on_this_suspend <simgrid::s4u::Exec::on_this_suspend_cb>`
+    :cpp:func:`Exec::on_resume <simgrid::s4u::Exec::on_resume_cb>`
+    :cpp:func:`Exec::on_this_resume <simgrid::s4u::Exec::on_this_resume_cb>`
+    :cpp:func:`Exec::on_veto <simgrid::s4u::Exec::on_veto_cb>`
+    :cpp:func:`Exec::on_this_veto <simgrid::s4u::Exec::on_this_veto_cb>`
+  - :cpp:func:`Io::on_start <simgrid::s4u::Io::on_start_cb>`
+    :cpp:func:`Io::on_this_start <simgrid::s4u::Io::on_this_start_cb>`
+    :cpp:func:`Io::on_completion <simgrid::s4u::Io::on_completion_cb>`
+    :cpp:func:`Io::on_this_completion <simgrid::s4u::Io::on_this_completion_cb>`
+    :cpp:func:`Io::on_suspend <simgrid::s4u::Io::on_suspend_cb>`
+    :cpp:func:`Io::on_this_suspend <simgrid::s4u::Io::on_this_suspend_cb>`
+    :cpp:func:`Io::on_resume <simgrid::s4u::Io::on_resume_cb>`
+    :cpp:func:`Io::on_this_resume <simgrid::s4u::Io::on_this_resume_cb>`
+    :cpp:func:`Io::on_veto <simgrid::s4u::Io::on_veto_cb>`
+    :cpp:func:`Io::on_this_veto <simgrid::s4u::Io::on_this_veto_cb>`
 
 Existing Plugins
 ****************
@@ -166,11 +219,18 @@ Battery
 
 .. doxygengroup:: plugin_battery
 
-.. _plugin_operation:
+.. _plugin_task:
+
+Task
+===========
+
+.. doxygengroup:: plugin_task
+
+.. _plugin_photovoltaic:
 
-Operation
+Photovoltaic
 ===========
 
-.. doxygengroup:: plugin_operation
+.. doxygengroup:: plugin_photovoltaic
 
 ..  LocalWords:  SimGrid
index 20f31c4..c339a00 100644 (file)
@@ -33,7 +33,7 @@ of source files.
    cmake_minimum_required(VERSION 2.8.12)
    project(MyFirstSimulator)
 
-   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
+   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
 
    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
    find_package(SimGrid REQUIRED)
@@ -124,7 +124,7 @@ Develop in C++ with Eclipse
 If you wish to develop your plugin or modify SimGrid using
 Eclipse. You have to run cmake and import it as a Makefile project.
 
-Next, you have to activate C++14 in your build settings, add -std=c++14
+Next, you have to activate C++17 in your build settings, add -std=c++17
 in the CDT GCC Built-in compiler settings.
 
 .. image:: /img/eclipseScreenShot.png
index 7d3d6b0..34045cb 100644 (file)
@@ -612,12 +612,19 @@ Signals
 
       .. doxygenfunction:: simgrid::s4u::Actor::on_creation_cb
       .. doxygenfunction:: simgrid::s4u::Actor::on_suspend_cb
+      .. doxygenfunction:: simgrid::s4u::Actor::on_this_suspend_cb
       .. doxygenfunction:: simgrid::s4u::Actor::on_host_change_cb
+      .. doxygenfunction:: simgrid::s4u::Actor::on_this_host_change_cb
       .. doxygenfunction:: simgrid::s4u::Actor::on_resume_cb
+      .. doxygenfunction:: simgrid::s4u::Actor::on_this_resume_cb
       .. doxygenfunction:: simgrid::s4u::Actor::on_sleep_cb
+      .. doxygenfunction:: simgrid::s4u::Actor::on_this_sleep_cb
       .. doxygenfunction:: simgrid::s4u::Actor::on_wake_up_cb
+      .. doxygenfunction:: simgrid::s4u::Actor::on_this_wake_up_cb
       .. doxygenfunction:: simgrid::s4u::Actor::on_termination_cb
+      .. doxygenfunction:: simgrid::s4u::Actor::on_this_termination_cb
       .. doxygenfunction:: simgrid::s4u::Actor::on_destruction_cb
+      .. doxygenfunction:: simgrid::s4u::Actor::on_this_destruction_cb
 
 .. _API_s4u_this_actor:
 
@@ -1205,7 +1212,9 @@ Signals
 
       .. doxygenfunction:: simgrid::s4u::Disk::on_creation_cb
       .. doxygenfunction:: simgrid::s4u::Disk::on_destruction_cb
-      .. doxygenfunction:: simgrid::s4u::Disk::on_state_change_cb
+      .. doxygenfunction:: simgrid::s4u::Disk::on_this_destruction_cb
+      .. doxygenfunction:: simgrid::s4u::Disk::on_onoff_cb
+      .. doxygenfunction:: simgrid::s4u::Disk::on_this_onoff_cb
 
 
 .. _API_s4u_Host:
@@ -1488,8 +1497,12 @@ Signals
 
       .. doxygenfunction:: simgrid::s4u::Host::on_creation_cb
       .. doxygenfunction:: simgrid::s4u::Host::on_destruction_cb
+      .. doxygenfunction:: simgrid::s4u::Host::on_this_destruction_cb
       .. doxygenfunction:: simgrid::s4u::Host::on_speed_change_cb
-      .. doxygenfunction:: simgrid::s4u::Host::on_state_change_cb
+      .. doxygenfunction:: simgrid::s4u::Host::on_this_speed_change_cb
+      .. doxygenfunction:: simgrid::s4u::Host::on_onoff_cb
+      .. doxygenfunction:: simgrid::s4u::Host::on_this_onoff_cb
+      .. doxygenfunction:: simgrid::s4u::Host::on_exec_state_change_cb
 
 .. _API_s4u_Link:
 
@@ -1700,10 +1713,13 @@ Signals
    .. group-tab:: C++
 
       .. doxygenfunction:: simgrid::s4u::Link::on_bandwidth_change_cb
+      .. doxygenfunction:: simgrid::s4u::Link::on_this_bandwidth_change_cb
       .. doxygenfunction:: simgrid::s4u::Link::on_communication_state_change_cb
       .. doxygenfunction:: simgrid::s4u::Link::on_creation_cb
       .. doxygenfunction:: simgrid::s4u::Link::on_destruction_cb
-      .. doxygenfunction:: simgrid::s4u::Link::on_state_change_cb
+      .. doxygenfunction:: simgrid::s4u::Link::on_this_destruction_cb
+      .. doxygenfunction:: simgrid::s4u::Link::on_onoff_cb
+      .. doxygenfunction:: simgrid::s4u::Link::on_this_onoff_cb
 
 .. _API_s4u_NetZone:
 
@@ -2046,13 +2062,21 @@ Signals
 
       .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_creation_cb
       .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_destruction_cb
+      .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_this_destruction_cb
       .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_migration_end_cb
+      .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_this_migration_end_cb
       .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_migration_start_cb
+      .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_this_migration_start_cb
       .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_resume_cb
+      .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_this_resume_cb
       .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_shutdown_cb
+      .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_this_shutdown_cb
       .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_start_cb
+      .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_this_start_cb
       .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_started_cb
+      .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_this_started_cb
       .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_suspend_cb
+      .. doxygenfunction:: simgrid::s4u::VirtualMachine::on_this_suspend_cb
 
 .. _API_s4u_Activity:
 
@@ -2126,17 +2150,6 @@ Suspending and resuming an activity
       .. doxygenfunction:: simgrid::s4u::Activity::resume
       .. doxygenfunction:: simgrid::s4u::Activity::is_suspended
 
-Signals
--------
-
-.. tabs::
-
-   .. group-tab:: C++
-
-      .. doxygenfunction:: simgrid::s4u::Activity::on_completion_cb
-      .. doxygenfunction:: simgrid::s4u::Activity::on_suspended_cb
-      .. doxygenfunction:: simgrid::s4u::Activity::on_resumed_cb
-
 .. _API_s4u_Comm:
 
 =============
@@ -2301,6 +2314,11 @@ Signals
       .. doxygenfunction:: simgrid::s4u::Comm::on_recv_cb
       .. doxygenfunction:: simgrid::s4u::Comm::on_send_cb
 
+      .. doxygenfunction:: simgrid::s4u::Comm::on_completion_cb
+      .. doxygenfunction:: simgrid::s4u::Comm::on_suspended_cb
+      .. doxygenfunction:: simgrid::s4u::Comm::on_resumed_cb
+      .. doxygenfunction:: simgrid::s4u::Comm::on_veto_cb
+
 .. _API_s4u_Exec:
 
 =============
@@ -2437,6 +2455,11 @@ Signals
       .. doxygenfunction:: simgrid::s4u::Exec::on_start_cb
       .. doxygenfunction:: simgrid::s4u::Exec::on_completion_cb
 
+      .. doxygenfunction:: simgrid::s4u::Exec::on_completion_cb
+      .. doxygenfunction:: simgrid::s4u::Exec::on_suspended_cb
+      .. doxygenfunction:: simgrid::s4u::Exec::on_resumed_cb
+      .. doxygenfunction:: simgrid::s4u::Exec::on_veto_cb
+
 .. _API_s4u_Io:
 
 ===========
@@ -2508,6 +2531,11 @@ Signals
       .. doxygenfunction:: simgrid::s4u::Io::on_start_cb
       .. doxygenfunction:: simgrid::s4u::Io::on_completion_cb
 
+      .. doxygenfunction:: simgrid::s4u::Io::on_completion_cb
+      .. doxygenfunction:: simgrid::s4u::Io::on_suspended_cb
+      .. doxygenfunction:: simgrid::s4u::Io::on_resumed_cb
+      .. doxygenfunction:: simgrid::s4u::Io::on_veto_cb
+
 .. _API_s4u_Synchronizations:
 
 =======================
index d346172..ea673f3 100644 (file)
@@ -1,7 +1,7 @@
 cmake_minimum_required(VERSION 2.8.12)
 project(tuto_disk)
 
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
 
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/" "../../../")
 find_package(SimGrid REQUIRED)
index 72660b4..d8a7314 100644 (file)
@@ -1,7 +1,7 @@
 cmake_minimum_required(VERSION 2.8.12)
 project(tuto_network)
 
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
 
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/" "../../../")
 find_package(SimGrid REQUIRED)
index 76dc61e..709bebe 100644 (file)
@@ -168,7 +168,8 @@ foreach (example activity-testany activity-waitany
                  mc-bugged1 mc-bugged1-liveness mc-bugged2 mc-bugged2-liveness mc-centralized-mutex mc-electric-fence mc-failing-assert
                  network-ns3 network-ns3-wifi network-wifi
                  io-async io-priority io-degradation io-file-system io-file-remote io-disk-raw io-dependent
-                 operation-simple operation-variable-load operation-switch-host
+                 task-io task-simple task-variable-load task-switch-host
+                 photovoltaic-simple
                  platform-comm-serialize platform-failures platform-profile platform-properties
                  plugin-host-load plugin-link-load plugin-prodcons
                  replay-comm replay-io
@@ -278,7 +279,7 @@ foreach(example mc-failing-assert)
                                       --setenv srcdir=${CMAKE_CURRENT_SOURCE_DIR}/${example}
                                       --cd ${CMAKE_CURRENT_SOURCE_DIR}/${example}
                                       ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}-nodpor.tesh)
-  endif()                                    
+  endif()
   set(tesh_files    ${tesh_files}   ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}-statequality.tesh)
   set(tesh_files    ${tesh_files}   ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}-nodpor.tesh)
 endforeach()
index 9e671b4..184e416 100644 (file)
@@ -13,24 +13,27 @@ namespace sg4 = simgrid::s4u;
 int main(int argc, char* argv[])
 {
   sg4::Engine e(&argc, argv);
-  sg_storage_file_system_init();
   e.load_platform(argv[1]);
 
   auto tremblay = e.host_by_name("Tremblay");
   auto jupiter  = e.host_by_name("Jupiter");
 
   // Display the details on vetoed activities
-  sg4::Activity::on_veto_cb([](const sg4::Activity& a) {
-    XBT_INFO("Activity '%s' vetoed. Dependencies: %s; Ressources: %s", a.get_cname(),
-             (a.dependencies_solved() ? "solved" : "NOT solved"), (a.is_assigned() ? "assigned" : "NOT assigned"));
+  sg4::Exec::on_veto_cb([](sg4::Exec const& exec) {
+    XBT_INFO("Execution '%s' vetoed. Dependencies: %s; Ressources: %s", exec.get_cname(),
+             (exec.dependencies_solved() ? "solved" : "NOT solved"), (exec.is_assigned() ? "assigned" : "NOT assigned"));
+  });
+  sg4::Comm::on_veto_cb([](sg4::Comm const& comm) {
+    XBT_INFO("Communication '%s' vetoed. Dependencies: %s; Ressources: %s", comm.get_cname(),
+             (comm.dependencies_solved() ? "solved" : "NOT solved"), (comm.is_assigned() ? "assigned" : "NOT assigned"));
   });
 
-  sg4::Activity::on_completion_cb([](sg4::Activity const& activity) {
-    if (const auto* exec = dynamic_cast<sg4::Exec const*>(&activity))
-      XBT_INFO("Activity '%s' is complete (start time: %f, finish time: %f)", exec->get_cname(), exec->get_start_time(),
-               exec->get_finish_time());
-    if (const auto* comm = dynamic_cast<sg4::Comm const*>(&activity))
-      XBT_INFO("Activity '%s' is complete", comm->get_cname());
+  sg4::Exec::on_completion_cb([](sg4::Exec const& exec) {
+    XBT_INFO("Exec '%s' is complete (start time: %f, finish time: %f)", exec.get_cname(), exec.get_start_time(),
+             exec.get_finish_time());
+  });
+  sg4::Comm::on_completion_cb([](sg4::Comm const& comm) {
+    XBT_INFO("Comm '%s' is complete", comm.get_cname());
   });
 
   // Create a small DAG: parent->transfer->child
index 2d000a0..abe40f4 100644 (file)
@@ -1,18 +1,18 @@
 #!/usr/bin/env tesh
 
 $ ${bindir:=.}/s4u-dag-comm ${platfdir}/two_hosts.xml --log=s4u_activity.t:verbose "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n"
-> [  0.000000] (0:maestro@) Activity 'parent' vetoed. Dependencies: solved; Ressources: NOT assigned
-> [  0.000000] (0:maestro@) Activity 'transfer' vetoed. Dependencies: NOT solved; Ressources: NOT assigned
-> [  0.000000] (0:maestro@) Activity 'child' vetoed. Dependencies: NOT solved; Ressources: NOT assigned
+> [  0.000000] (0:maestro@) Execution 'parent' vetoed. Dependencies: solved; Ressources: NOT assigned
+> [  0.000000] (0:maestro@) Communication 'transfer' vetoed. Dependencies: NOT solved; Ressources: NOT assigned
+> [  0.000000] (0:maestro@) Execution 'child' vetoed. Dependencies: NOT solved; Ressources: NOT assigned
 > [  0.000000] (0:maestro@) 'parent' is assigned to a resource and all dependencies are solved. Let's start
-> [  0.000000] (0:maestro@) Activity 'transfer' vetoed. Dependencies: NOT solved; Ressources: NOT assigned
-> [  0.000000] (0:maestro@) Activity 'child' vetoed. Dependencies: NOT solved; Ressources: assigned
-> [  0.000000] (0:maestro@) Activity 'transfer' vetoed. Dependencies: NOT solved; Ressources: assigned
-> [  1.000000] (0:maestro@) Activity 'parent' is complete (start time: 0.000000, finish time: 1.000000)
+> [  0.000000] (0:maestro@) Communication 'transfer' vetoed. Dependencies: NOT solved; Ressources: NOT assigned
+> [  0.000000] (0:maestro@) Execution 'child' vetoed. Dependencies: NOT solved; Ressources: assigned
+> [  0.000000] (0:maestro@) Communication 'transfer' vetoed. Dependencies: NOT solved; Ressources: assigned
+> [  1.000000] (0:maestro@) Exec 'parent' is complete (start time: 0.000000, finish time: 1.000000)
 > [  1.000000] (0:maestro@) Remove a dependency from 'parent' on 'transfer'
 > [  1.000000] (0:maestro@) 'transfer' is assigned to a resource and all dependencies are solved. Let's start
-> [  2.083775] (0:maestro@) Activity 'transfer' is complete
 > [  2.083775] (0:maestro@) Remove a dependency from 'transfer' on 'child'
 > [  2.083775] (0:maestro@) 'child' is assigned to a resource and all dependencies are solved. Let's start
-> [  3.083775] (0:maestro@) Activity 'child' is complete (start time: 2.083775, finish time: 3.083775)
+> [  2.083775] (0:maestro@) Comm 'transfer' is complete
+> [  3.083775] (0:maestro@) Exec 'child' is complete (start time: 2.083775, finish time: 3.083775)
 > [  3.083775] (0:maestro@) Simulation time 3.08378
index 631b24c..c2ab8d5 100644 (file)
@@ -18,19 +18,16 @@ int main(int argc, char** argv)
 
   auto* faulty = e.host_by_name("Faulty Host");
   auto* safe   = e.host_by_name("Safe Host");
-  sg4::Activity::on_completion_cb([](sg4::Activity const& activity) {
-    const auto* exec = dynamic_cast<sg4::Exec const*>(&activity);
-    if (exec == nullptr) // Only Execs are concerned here
-      return;
-    if (exec->get_state() == sg4::Activity::State::FINISHED)
-      XBT_INFO("Activity '%s' is complete (start time: %f, finish time: %f)", exec->get_cname(), exec->get_start_time(),
-               exec->get_finish_time());
-    if (exec->get_state() == sg4::Activity::State::FAILED) {
-      if (exec->is_parallel())
-        XBT_INFO("Activity '%s' has failed. %.f %% remain to be done", exec->get_cname(),
-                 100 * exec->get_remaining_ratio());
+  sg4::Exec::on_completion_cb([](sg4::Exec const& exec) {
+    if (exec.get_state() == sg4::Activity::State::FINISHED)
+      XBT_INFO("Activity '%s' is complete (start time: %f, finish time: %f)", exec.get_cname(), exec.get_start_time(),
+               exec.get_finish_time());
+    if (exec.get_state() == sg4::Activity::State::FAILED) {
+      if (exec.is_parallel())
+        XBT_INFO("Activity '%s' has failed. %.f %% remain to be done", exec.get_cname(),
+                 100 * exec.get_remaining_ratio());
       else
-        XBT_INFO("Activity '%s' has failed. %.f flops remain to be done", exec->get_cname(), exec->get_remaining());
+        XBT_INFO("Activity '%s' has failed. %.f flops remain to be done", exec.get_cname(), exec.get_remaining());
     }
   });
 
index d55ec54..950cd77 100644 (file)
@@ -32,9 +32,14 @@ int main(int argc, char* argv[])
     }
   }
 
-  simgrid::s4u::Activity::on_completion_cb([](simgrid::s4u::Activity const& activity) {
-    XBT_INFO("Activity '%s' is complete (start time: %f, finish time: %f)", activity.get_cname(),
-             activity.get_start_time(), activity.get_finish_time());
+  simgrid::s4u::Exec::on_completion_cb([](simgrid::s4u::Exec const& exec) {
+    XBT_INFO("Exec '%s' is complete (start time: %f, finish time: %f)", exec.get_cname(),
+             exec.get_start_time(), exec.get_finish_time());
+  });
+
+  simgrid::s4u::Comm::on_completion_cb([](simgrid::s4u::Comm const& comm) {
+    XBT_INFO("Comm '%s' is complete (start time: %f, finish time: %f)", comm.get_cname(),
+             comm.get_start_time(), comm.get_finish_time());
   });
 
   e.run();
index 7e78194..af5a6f2 100644 (file)
@@ -1,12 +1,12 @@
 #!/usr/bin/env tesh
 
 $ ${bindir:=.}/s4u-dag-from-dax-simple --log=no_loc ${platfdir}/small_platform.xml ${srcdir:=.}/dag.xml
-> [0.000000] [dag_from_dax_simple/INFO] Activity 'root' is complete (start time: 0.000000, finish time: 0.000000)
-> [33.394394] [dag_from_dax_simple/INFO] Activity 'root_i2_2@c2' is complete (start time: 0.000000, finish time: 33.394394)
-> [39.832311] [dag_from_dax_simple/INFO] Activity 'root_i1_1@c1' is complete (start time: 0.000000, finish time: 39.832311)
-> [467.988690] [dag_from_dax_simple/INFO] Activity '1@c1' is complete (start time: 39.832311, finish time: 467.988690)
-> [543.077868] [dag_from_dax_simple/INFO] Activity '1@c1_o1_3@c3' is complete (start time: 467.988690, finish time: 543.077868)
-> [2785.832267] [dag_from_dax_simple/INFO] Activity '2@c2' is complete (start time: 33.394394, finish time: 2785.832267)
-> [3886.807417] [dag_from_dax_simple/INFO] Activity '3@c3' is complete (start time: 2785.832267, finish time: 3886.807417)
-> [3887.221639] [dag_from_dax_simple/INFO] Activity '3@c3_o3_end' is complete (start time: 3886.807417, finish time: 3887.221639)
-> [3887.221639] [dag_from_dax_simple/INFO] Activity 'end' is complete (start time: 3887.221639, finish time: 3887.221639)
\ No newline at end of file
+> [0.000000] [dag_from_dax_simple/INFO] Exec 'root' is complete (start time: 0.000000, finish time: 0.000000)
+> [33.394394] [dag_from_dax_simple/INFO] Comm 'root_i2_2@c2' is complete (start time: 0.000000, finish time: 33.394394)
+> [39.832311] [dag_from_dax_simple/INFO] Comm 'root_i1_1@c1' is complete (start time: 0.000000, finish time: 39.832311)
+> [467.988690] [dag_from_dax_simple/INFO] Exec '1@c1' is complete (start time: 39.832311, finish time: 467.988690)
+> [543.077868] [dag_from_dax_simple/INFO] Comm '1@c1_o1_3@c3' is complete (start time: 467.988690, finish time: 543.077868)
+> [2785.832267] [dag_from_dax_simple/INFO] Exec '2@c2' is complete (start time: 33.394394, finish time: 2785.832267)
+> [3886.807417] [dag_from_dax_simple/INFO] Exec '3@c3' is complete (start time: 2785.832267, finish time: 3886.807417)
+> [3887.221639] [dag_from_dax_simple/INFO] Comm '3@c3_o3_end' is complete (start time: 3886.807417, finish time: 3887.221639)
+> [3887.221639] [dag_from_dax_simple/INFO] Exec 'end' is complete (start time: 3887.221639, finish time: 3887.221639)
\ No newline at end of file
index 5fbcf2c..a5532eb 100644 (file)
@@ -32,9 +32,14 @@ int main(int argc, char* argv[])
     }
   }
 
-  simgrid::s4u::Activity::on_completion_cb([](simgrid::s4u::Activity const& activity) {
-    XBT_INFO("Activity '%s' is complete (start time: %f, finish time: %f)", activity.get_cname(),
-             activity.get_start_time(), activity.get_finish_time());
+  simgrid::s4u::Exec::on_completion_cb([](simgrid::s4u::Exec const& exec) {
+    XBT_INFO("Exec '%s' is complete (start time: %f, finish time: %f)", exec.get_cname(),
+             exec.get_start_time(), exec.get_finish_time());
+  });
+
+  simgrid::s4u::Comm::on_completion_cb([](simgrid::s4u::Comm const& comm) {
+    XBT_INFO("Comm '%s' is complete (start time: %f, finish time: %f)", comm.get_cname(),
+             comm.get_start_time(), comm.get_finish_time());
   });
 
   e.run();
index d6a88d5..6133b1b 100644 (file)
@@ -1,12 +1,12 @@
 #!/usr/bin/env tesh
 
 $ ${bindir:=.}/s4u-dag-from-dot-simple --log=no_loc ${platfdir}/small_platform.xml ${srcdir:=.}/dag.dot
-> [0.000000] [dag_from_dot_simple/INFO] Activity 'root' is complete (start time: 0.000000, finish time: 0.000000)
-> [33.394394] [dag_from_dot_simple/INFO] Activity 'root->c2' is complete (start time: 0.000000, finish time: 33.394394)
-> [39.832311] [dag_from_dot_simple/INFO] Activity 'root->c1' is complete (start time: 0.000000, finish time: 39.832311)
-> [50.026511] [dag_from_dot_simple/INFO] Activity 'c1' is complete (start time: 39.832311, finish time: 50.026511)
-> [98.928629] [dag_from_dot_simple/INFO] Activity 'c2' is complete (start time: 33.394394, finish time: 98.928629)
-> [125.115689] [dag_from_dot_simple/INFO] Activity 'c1->c3' is complete (start time: 50.026511, finish time: 125.115689)
-> [151.329383] [dag_from_dot_simple/INFO] Activity 'c3' is complete (start time: 125.115689, finish time: 151.329383)
-> [151.743605] [dag_from_dot_simple/INFO] Activity 'c3->end' is complete (start time: 151.329383, finish time: 151.743605)
-> [151.743605] [dag_from_dot_simple/INFO] Activity 'end' is complete (start time: 151.743605, finish time: 151.743605)
\ No newline at end of file
+> [0.000000] [dag_from_dot_simple/INFO] Exec 'root' is complete (start time: 0.000000, finish time: 0.000000)
+> [33.394394] [dag_from_dot_simple/INFO] Comm 'root->c2' is complete (start time: 0.000000, finish time: 33.394394)
+> [39.832311] [dag_from_dot_simple/INFO] Comm 'root->c1' is complete (start time: 0.000000, finish time: 39.832311)
+> [50.026511] [dag_from_dot_simple/INFO] Exec 'c1' is complete (start time: 39.832311, finish time: 50.026511)
+> [98.928629] [dag_from_dot_simple/INFO] Exec 'c2' is complete (start time: 33.394394, finish time: 98.928629)
+> [125.115689] [dag_from_dot_simple/INFO] Comm 'c1->c3' is complete (start time: 50.026511, finish time: 125.115689)
+> [151.329383] [dag_from_dot_simple/INFO] Exec 'c3' is complete (start time: 125.115689, finish time: 151.329383)
+> [151.743605] [dag_from_dot_simple/INFO] Comm 'c3->end' is complete (start time: 151.329383, finish time: 151.743605)
+> [151.743605] [dag_from_dot_simple/INFO] Exec 'end' is complete (start time: 151.743605, finish time: 151.743605)
\ No newline at end of file
index e6246b1..bf87897 100644 (file)
@@ -14,9 +14,14 @@ int main(int argc, char* argv[])
 
   std::vector<simgrid::s4u::ActivityPtr> dag = simgrid::s4u::create_DAG_from_json(argv[2]);
 
-  simgrid::s4u::Activity::on_completion_cb([](simgrid::s4u::Activity const& activity) {
-    XBT_INFO("Activity '%s' is complete (start time: %f, finish time: %f)", activity.get_cname(),
-             activity.get_start_time(), activity.get_finish_time());
+   simgrid::s4u::Exec::on_completion_cb([](simgrid::s4u::Exec const& exec) {
+    XBT_INFO("Exec '%s' is complete (start time: %f, finish time: %f)", exec.get_cname(),
+             exec.get_start_time(), exec.get_finish_time());
+  });
+
+  simgrid::s4u::Comm::on_completion_cb([](simgrid::s4u::Comm const& comm) {
+    XBT_INFO("Comm '%s' is complete (start time: %f, finish time: %f)", comm.get_cname(),
+             comm.get_start_time(), comm.get_finish_time());
   });
 
   e.run();
index 1d37a65..cd41320 100644 (file)
@@ -1,7 +1,7 @@
 #!/usr/bin/env tesh
 
 $ ${bindir:=.}/s4u-dag-from-json-simple --log=no_loc ${platfdir}/small_platform.xml ${srcdir:=.}/dag.json
-> [10.194200] [dag_from_json_simple/INFO] Activity 'c1' is complete (start time: 0.000000, finish time: 10.194200)
-> [65.534235] [dag_from_json_simple/INFO] Activity 'c2' is complete (start time: 0.000000, finish time: 65.534235)
-> [85.283378] [dag_from_json_simple/INFO] Activity 't1' is complete (start time: 10.194200, finish time: 85.283378)
-> [111.497072] [dag_from_json_simple/INFO] Activity 'c3' is complete (start time: 85.283378, finish time: 111.497072)
\ No newline at end of file
+> [10.194200] [dag_from_json_simple/INFO] Exec 'c1' is complete (start time: 0.000000, finish time: 10.194200)
+> [65.534235] [dag_from_json_simple/INFO] Exec 'c2' is complete (start time: 0.000000, finish time: 65.534235)
+> [85.283378] [dag_from_json_simple/INFO] Comm 't1' is complete (start time: 10.194200, finish time: 85.283378)
+> [111.497072] [dag_from_json_simple/INFO] Exec 'c3' is complete (start time: 85.283378, finish time: 111.497072)
\ No newline at end of file
index ee00eb0..0ca2de2 100644 (file)
@@ -13,24 +13,24 @@ namespace sg4 = simgrid::s4u;
 int main(int argc, char* argv[])
 {
   sg4::Engine e(&argc, argv);
-  sg_storage_file_system_init();
   e.load_platform(argv[1]);
 
   auto bob  = e.host_by_name("bob");
   auto carl = e.host_by_name("carl");
 
   // Display the details on vetoed activities
-  sg4::Activity::on_veto_cb([](const sg4::Activity& a) {
-    XBT_INFO("Activity '%s' vetoed. Dependencies: %s; Ressources: %s", a.get_cname(),
-             (a.dependencies_solved() ? "solved" : "NOT solved"), (a.is_assigned() ? "assigned" : "NOT assigned"));
+  sg4::Exec::on_veto_cb([](sg4::Exec const& exec) {
+    XBT_INFO("Exec '%s' vetoed. Dependencies: %s; Ressources: %s", exec.get_cname(),
+             (exec.dependencies_solved() ? "solved" : "NOT solved"), (exec.is_assigned() ? "assigned" : "NOT assigned"));
+  });
+  sg4::Io::on_veto_cb([](sg4::Io const& io) {
+    XBT_INFO("Io '%s' vetoed. Dependencies: %s; Ressources: %s", io.get_cname(),
+             (io.dependencies_solved() ? "solved" : "NOT solved"), (io.is_assigned() ? "assigned" : "NOT assigned"));
   });
 
-  sg4::Activity::on_completion_cb([](sg4::Activity const& activity) {
-    const auto* exec = dynamic_cast<sg4::Exec const*>(&activity);
-    if (exec == nullptr) // Only Execs are concerned here
-      return;
-    XBT_INFO("Activity '%s' is complete (start time: %f, finish time: %f)", exec->get_cname(), exec->get_start_time(),
-             exec->get_finish_time());
+  sg4::Exec::on_completion_cb([](sg4::Exec const& exec) {
+    XBT_INFO("Exec '%s' is complete (start time: %f, finish time: %f)", exec.get_cname(), exec.get_start_time(),
+             exec.get_finish_time());
   });
 
   // Create a small DAG: parent->write_output->read_input->child
index 186126b..8c1c3ca 100644 (file)
@@ -2,15 +2,15 @@
 
 $ ${bindir:=.}/s4u-dag-io ${platfdir}/hosts_with_disks.xml --log=s4u_activity.t:verbose "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n"
 > [  0.000000] (0:maestro@) 'parent' is assigned to a resource and all dependencies are solved. Let's start
-> [  0.000000] (0:maestro@) Activity 'write' vetoed. Dependencies: NOT solved; Ressources: assigned
-> [  0.000000] (0:maestro@) Activity 'read' vetoed. Dependencies: NOT solved; Ressources: assigned
-> [  0.000000] (0:maestro@) Activity 'child' vetoed. Dependencies: NOT solved; Ressources: assigned
-> [  1.000000] (0:maestro@) Activity 'parent' is complete (start time: 0.000000, finish time: 1.000000)
+> [  0.000000] (0:maestro@) Io 'write' vetoed. Dependencies: NOT solved; Ressources: assigned
+> [  0.000000] (0:maestro@) Io 'read' vetoed. Dependencies: NOT solved; Ressources: assigned
+> [  0.000000] (0:maestro@) Exec 'child' vetoed. Dependencies: NOT solved; Ressources: assigned
+> [  1.000000] (0:maestro@) Exec 'parent' is complete (start time: 0.000000, finish time: 1.000000)
 > [  1.000000] (0:maestro@) Remove a dependency from 'parent' on 'write'
 > [  1.000000] (0:maestro@) 'write' is assigned to a resource and all dependencies are solved. Let's start
 > [ 26.000000] (0:maestro@) Remove a dependency from 'write' on 'read'
 > [ 26.000000] (0:maestro@) 'read' is assigned to a resource and all dependencies are solved. Let's start
 > [ 36.000000] (0:maestro@) Remove a dependency from 'read' on 'child'
 > [ 36.000000] (0:maestro@) 'child' is assigned to a resource and all dependencies are solved. Let's start
-> [ 37.000000] (0:maestro@) Activity 'child' is complete (start time: 36.000000, finish time: 37.000000)
+> [ 37.000000] (0:maestro@) Exec 'child' is complete (start time: 36.000000, finish time: 37.000000)
 > [ 37.000000] (0:maestro@) Simulation time 37
index 5386b58..7b76aa9 100644 (file)
@@ -66,11 +66,11 @@ static sg4::Host* get_best_host(const sg4::ExecPtr exec, double* min_finish_time
         // We use the user data field to store the finish time of the predecessor of the comm, i.e., its potential
         // start time
         data_available = *comm->get_data<double>() + redist_time;
-     }
+      }
 
       /* no transfer, control dependency */
-      if (const auto* exec = dynamic_cast<sg4::Exec*>(parent.get()))
-        data_available = exec->get_finish_time();
+      if (const auto* parent_exec = dynamic_cast<sg4::Exec*>(parent.get()))
+        data_available = parent_exec->get_finish_time();
 
       if (last_data_available < data_available)
         last_data_available = data_available;
@@ -118,15 +118,12 @@ int main(int argc, char** argv)
   std::set<sg4::Activity*> vetoed;
   e.track_vetoed_activities(&vetoed);
 
-  sg4::Activity::on_completion_cb([](sg4::Activity const& activity) {
+  sg4::Exec::on_completion_cb([](sg4::Exec const& exec) {
     // when an Exec completes, we need to set the potential start time of all its ouput comms
-    const auto* exec = dynamic_cast<sg4::Exec const*>(&activity);
-    if (exec == nullptr) // Only Execs are concerned here
-      return;
-    for (const auto& succ : exec->get_successors()) {
+    for (const auto& succ : exec.get_successors()) {
       auto* comm = dynamic_cast<sg4::Comm*>(succ.get());
       if (comm != nullptr) {
-        auto* finish_time = new double(exec->get_finish_time());
+        auto* finish_time = new double(exec.get_finish_time());
         // We use the user data field to store the finish time of the predecessor of the comm, i.e., its potential start
         // time
         comm->set_data(finish_time);
@@ -148,9 +145,9 @@ int main(int argc, char** argv)
   auto dax = sg4::create_DAG_from_DAX(argv[2]);
 
   /* Schedule the root first */
-  double finish_time;
+  double root_finish_time;
   auto* root = static_cast<sg4::Exec*>(dax.front().get());
-  auto host  = get_best_host(root, &finish_time);
+  auto* host = get_best_host(root, &root_finish_time);
   schedule_on(root, host);
 
   e.run();
@@ -193,7 +190,7 @@ int main(int argc, char** argv)
   }
 
   /* Cleanup memory */
-  for (auto const& h : e.get_all_hosts())
+  for (auto const* h : e.get_all_hosts())
     delete h->get_data<double>();
 
   XBT_INFO("Simulation Time: %f", simgrid_get_clock());
index aae95e7..78a0158 100644 (file)
@@ -19,20 +19,15 @@ int main(int argc, char* argv[])
   auto fafard = e.host_by_name("Fafard");
 
   // Display the details on vetoed activities
-  sg4::Activity::on_veto_cb([](const sg4::Activity& a) {
-    const auto& exec = static_cast<const sg4::Exec&>(a); // all activities are execs in this example
-
-    XBT_INFO("Activity '%s' vetoed. Dependencies: %s; Ressources: %s", exec.get_cname(),
+  sg4::Exec::on_veto_cb([](sg4::Exec const& exec) {
+    XBT_INFO("Execution '%s' vetoed. Dependencies: %s; Ressources: %s", exec.get_cname(),
              (exec.dependencies_solved() ? "solved" : "NOT solved"),
              (exec.is_assigned() ? "assigned" : "NOT assigned"));
   });
 
-  sg4::Activity::on_completion_cb([](sg4::Activity const& activity) {
-    const auto* exec = dynamic_cast<sg4::Exec const*>(&activity);
-    if (exec == nullptr) // Only Execs are concerned here
-      return;
-    XBT_INFO("Activity '%s' is complete (start time: %f, finish time: %f)", exec->get_cname(), exec->get_start_time(),
-             exec->get_finish_time());
+  sg4::Exec::on_completion_cb([](sg4::Exec const& exec) {
+    XBT_INFO("Execution '%s' is complete (start time: %f, finish time: %f)", exec.get_cname(), exec.get_start_time(),
+             exec.get_finish_time());
   });
 
   // Define an amount of work that should take 1 second to execute.
index e41d10e..18b0d0b 100644 (file)
@@ -3,14 +3,14 @@
 $ ${bindir:=.}/s4u-dag-simple ${platfdir}/small_platform.xml --log=s4u_activity.t:verbose "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n"
 > [  0.000000] (0:maestro@) 'parent 1' is assigned to a resource and all dependencies are solved. Let's start
 > [  0.000000] (0:maestro@) 'parent 2' is assigned to a resource and all dependencies are solved. Let's start
-> [  0.000000] (0:maestro@) Activity 'child' vetoed. Dependencies: NOT solved; Ressources: NOT assigned
-> [  2.000000] (0:maestro@) Activity 'parent 1' is complete (start time: 0.000000, finish time: 2.000000)
+> [  0.000000] (0:maestro@) Execution 'child' vetoed. Dependencies: NOT solved; Ressources: NOT assigned
+> [  2.000000] (0:maestro@) Execution 'parent 1' is complete (start time: 0.000000, finish time: 2.000000)
 > [  2.000000] (0:maestro@) Remove a dependency from 'parent 1' on 'child'
 > [  2.000000] (0:maestro@) Activity child not ready.
-> [  3.000000] (0:maestro@) Activity 'parent 2' is complete (start time: 0.000000, finish time: 3.000000)
+> [  3.000000] (0:maestro@) Execution 'parent 2' is complete (start time: 0.000000, finish time: 3.000000)
 > [  3.000000] (0:maestro@) Remove a dependency from 'parent 2' on 'child'
-> [  3.000000] (0:maestro@) Activity 'child' vetoed. Dependencies: solved; Ressources: NOT assigned
+> [  3.000000] (0:maestro@) Execution 'child' vetoed. Dependencies: solved; Ressources: NOT assigned
 > [  3.000000] (0:maestro@) Activity child's dependencies are resolved. Let's assign it to Fafard.
 > [  3.000000] (0:maestro@) 'child' is assigned to a resource and all dependencies are solved. Let's start
-> [  4.000000] (0:maestro@) Activity 'child' is complete (start time: 3.000000, finish time: 4.000000)
+> [  4.000000] (0:maestro@) Execution 'child' is complete (start time: 3.000000, finish time: 4.000000)
 > [  4.000000] (0:maestro@) Simulation time 4
index 39b56d6..f90c9f9 100644 (file)
@@ -43,9 +43,13 @@ int main(int argc, char* argv[])
   c1->start();
   c2->start();
 
-  simgrid::s4u::Activity::on_completion_cb([](simgrid::s4u::Activity const& activity) {
-    XBT_INFO("Activity '%s' is complete (start time: %f, finish time: %f)", activity.get_cname(),
-             activity.get_start_time(), activity.get_finish_time());
+  simgrid::s4u::Exec::on_completion_cb([](simgrid::s4u::Exec const& exec) {
+    XBT_INFO("Exec '%s' is complete (start time: %f, finish time: %f)", exec.get_cname(),
+             exec.get_start_time(), exec.get_finish_time());
+  });
+ simgrid::s4u::Comm::on_completion_cb([](simgrid::s4u::Comm const& comm) {
+    XBT_INFO("Comm '%s' is complete (start time: %f, finish time: %f)", comm.get_cname(),
+             comm.get_start_time(), comm.get_finish_time());
   });
 
   e.run();
index 56e448f..f5b8b15 100644 (file)
@@ -1,7 +1,7 @@
 #!/usr/bin/env tesh
 
 $ ${bindir:=.}/s4u-dag-tuto --log=no_loc ${platfdir}/small_platform.xml
-> [10.194200] [dag_tuto/INFO] Activity 'c1' is complete (start time: 0.000000, finish time: 10.194200)
-> [65.534235] [dag_tuto/INFO] Activity 'c2' is complete (start time: 0.000000, finish time: 65.534235)
-> [85.283378] [dag_tuto/INFO] Activity 't1' is complete (start time: 10.194200, finish time: 85.283378)
-> [111.497072] [dag_tuto/INFO] Activity 'c3' is complete (start time: 85.283378, finish time: 111.497072)
\ No newline at end of file
+> [10.194200] [dag_tuto/INFO] Exec 'c1' is complete (start time: 0.000000, finish time: 10.194200)
+> [65.534235] [dag_tuto/INFO] Exec 'c2' is complete (start time: 0.000000, finish time: 65.534235)
+> [85.283378] [dag_tuto/INFO] Comm 't1' is complete (start time: 10.194200, finish time: 85.283378)
+> [111.497072] [dag_tuto/INFO] Exec 'c3' is complete (start time: 85.283378, finish time: 111.497072)
\ No newline at end of file
index 926b692..4840f29 100644 (file)
@@ -6,6 +6,7 @@
 
 #ifndef _KADEMLIA_TASK_HPP_
 #define _KADEMLIA_TASK_HPP_
+#include "answer.hpp"
 #include "s4u-dht-kademlia.hpp"
 #include "simgrid/s4u.hpp"
 
index e019ddd..0ef4253 100644 (file)
@@ -141,10 +141,10 @@ static void runner()
 
   // ========= A new ptask with computation and a timeout =========
   start = sg4::Engine::get_clock();
-  std::vector<double> cpu_amounts5{flopAmount, flopAmount};
-  std::vector<double> com_amounts5{0, 0, 0, 0};
   XBT_INFO("Run a task with computation on two hosts and a timeout of 20s.");
   try {
+    std::vector<double> cpu_amounts5{flopAmount, flopAmount};
+    std::vector<double> com_amounts5{0, 0, 0, 0};
     sg4::this_actor::exec_init(hosts, cpu_amounts5, com_amounts5)->wait_for(20);
   } catch (const simgrid::TimeoutException &){
     XBT_INFO("Finished WITH timeout");
index a62cfd0..23e67b7 100644 (file)
@@ -54,8 +54,7 @@ int main(int argc, char* argv[])
 
   sg4::Actor::create("worker", e.host_by_name("Fafard"), worker);
 
-  sg4::Activity::on_veto_cb([&e](sg4::Activity& a) {
-    auto& exec = static_cast<sg4::Exec&>(a);
+  sg4::Exec::on_veto_cb([&e](sg4::Exec& exec) {
 
     // First display the situation
     XBT_INFO("Activity '%s' vetoed. Dependencies: %s; Ressources: %s", exec.get_cname(),
index 8788e10..c86a78c 100644 (file)
@@ -54,7 +54,6 @@ static void test()
 int main(int argc, char* argv[])
 {
   sg4::Engine e(&argc, argv);
-  sg_storage_file_system_init();
   e.load_platform(argv[1]);
 
   sg4::Actor::create("bob", e.host_by_name("bob"), test);
index 653cbf9..56813fd 100644 (file)
@@ -16,7 +16,7 @@ $ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-c
 > [0.000000] [mc_explo/INFO]   3: iSend(mbox=0)
 > [0.000000] [mc_explo/INFO]   1: WaitComm(from 3 to 1, mbox=0, no timeout)
 > [0.000000] [mc_explo/INFO] You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1;3;1;1;3;3;1'
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 551 unique states visited; 137 backtracks (2239 transition replays, 1551 states visited overall)
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1070 unique states visited; 252 backtracks (4082 transition replays, 2760 states visited overall)
 
 ! expect return 1
 ! timeout 20
@@ -26,12 +26,13 @@ $ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-c
 > [0.000000] [mc_explo/INFO] *** PROPERTY NOT VALID ***
 > [0.000000] [mc_explo/INFO] **************************
 > [0.000000] [mc_explo/INFO] Counter-example execution trace:
-> [0.000000] [mc_explo/INFO]   3: iSend(mbox=0)
 > [0.000000] [mc_explo/INFO]   1: iRecv(mbox=0)
+> [0.000000] [mc_explo/INFO]   3: iSend(mbox=0)
 > [0.000000] [mc_explo/INFO]   1: WaitComm(from 3 to 1, mbox=0, no timeout)
 > [0.000000] [mc_explo/INFO]   1: iRecv(mbox=0)
 > [0.000000] [mc_explo/INFO]   3: WaitComm(from 3 to 1, mbox=0, no timeout)
 > [0.000000] [mc_explo/INFO]   3: iSend(mbox=0)
 > [0.000000] [mc_explo/INFO]   1: WaitComm(from 3 to 1, mbox=0, no timeout)
-> [0.000000] [mc_explo/INFO] You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'3;1;1;1;3;3;1'
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1161 unique states visited; 282 backtracks (4556 transition replays, 3113 states visited overall)
+> [0.000000] [mc_explo/INFO] You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1;3;1;1;3;3;1'
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 995 unique states visited; 253 backtracks (4006 transition replays, 2758 states visited overall)
+
index aede081..23ec6c5 100644 (file)
@@ -46,6 +46,30 @@ $ ${bindir:=.}/s4u-network-ns3 ${platfdir}/onelink.xml ${srcdir}/onelink_d.xml -
 > [:maestro(0) 0.000000] [xbt_cfg/INFO] Configuration change: Set 'network/model' to 'ns-3'
 > [C1:worker(2) 1.104600] [s4u_test/INFO] FLOW[1] : Receive 10000 bytes from S1 to C1
 
+p 2hosts 1link NewReno (no timing change)
+
+$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/onelink.xml ${srcdir}/onelink_d.xml --cfg=network/model:ns-3 --cfg=ns3/NetworkModel:NewReno "--log=root.fmt:[%h:%a(%i)%e%r]%e[%c/%p]%e%m%n"
+> [:maestro(0) 0.000000] [xbt_cfg/INFO] Configuration change: Set 'network/model' to 'ns-3'
+> [:maestro(0) 0.000000] [xbt_cfg/INFO] Configuration change: Set 'ns3/NetworkModel' to 'NewReno'
+> [:maestro(0) 0.000000] [res_ns3/INFO] Switching Tcp protocol to 'NewReno'
+> [C1:worker(2) 1.104600] [s4u_test/INFO] FLOW[1] : Receive 10000 bytes from S1 to C1
+
+p 2hosts 1link Cubic (no timing change)
+
+$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/onelink.xml ${srcdir}/onelink_d.xml --cfg=network/model:ns-3 --cfg=ns3/NetworkModel:Cubic "--log=root.fmt:[%h:%a(%i)%e%r]%e[%c/%p]%e%m%n"
+> [:maestro(0) 0.000000] [xbt_cfg/INFO] Configuration change: Set 'network/model' to 'ns-3'
+> [:maestro(0) 0.000000] [xbt_cfg/INFO] Configuration change: Set 'ns3/NetworkModel' to 'Cubic'
+> [:maestro(0) 0.000000] [res_ns3/INFO] Switching Tcp protocol to 'Cubic'
+> [C1:worker(2) 1.104600] [s4u_test/INFO] FLOW[1] : Receive 10000 bytes from S1 to C1
+
+p 2hosts 1link UDP
+
+# $ ${bindir:=.}/s4u-network-ns3 ${platfdir}/onelink.xml ${srcdir}/onelink_d.xml --cfg=network/model:ns-3 --cfg=ns3/NetworkModel:UDP "--log=root.fmt:[%h:%a(%i)%e%r]%e[%c/%p]%e%m%n"
+# > [:maestro(0) 0.000000] [xbt_cfg/INFO] Configuration change: Set 'network/model' to 'ns-3'
+# > [:maestro(0) 0.000000] [xbt_cfg/INFO] Configuration change: Set 'ns3/NetworkModel' to 'UDP'
+# > [:maestro(0) 0.000000] [res_ns3/INFO] Switching network protocol to 'UDP'
+# > [C1:worker(2) 1.104600] [s4u_test/INFO] FLOW[1] : Receive 10000 bytes from S1 to C1
+
 p Crosstraffic TCP option DISABLED
 $ ${bindir:=.}/s4u-network-ns3 ${platfdir}/crosstraffic.xml ${srcdir}/crosstraffic_d.xml --cfg=network/model:ns-3 --cfg=network/crosstraffic:0
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'network/model' to 'ns-3'
diff --git a/examples/cpp/operation-simple/s4u-operation-simple.cpp b/examples/cpp/operation-simple/s4u-operation-simple.cpp
deleted file mode 100644 (file)
index 40a32b2..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Copyright (c) 2017-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. */
-
-/* This example demonstrate basic use of the operation plugin.
- *
- * We model the following graph:
- *
- * exec1 -> comm -> exec2
- *
- * exec1 and exec2 are execution operations.
- * comm is a communication operation.
- */
-
-#include "simgrid/plugins/operation.hpp"
-#include "simgrid/s4u.hpp"
-
-XBT_LOG_NEW_DEFAULT_CATEGORY(operation_simple, "Messages specific for this operation example");
-
-int main(int argc, char* argv[])
-{
-  simgrid::s4u::Engine e(&argc, argv);
-  e.load_platform(argv[1]);
-  simgrid::plugins::Operation::init();
-
-  // Retrieve hosts
-  auto tremblay = e.host_by_name("Tremblay");
-  auto jupiter  = e.host_by_name("Jupiter");
-
-  // Create operations
-  auto exec1 = simgrid::plugins::ExecOp::init("exec1", 1e9, tremblay);
-  auto exec2 = simgrid::plugins::ExecOp::init("exec2", 1e9, jupiter);
-  auto comm  = simgrid::plugins::CommOp::init("comm", 1e7, tremblay, jupiter);
-
-  // Create the graph by defining dependencies between operations
-  exec1->add_successor(comm);
-  comm->add_successor(exec2);
-
-  // Add a function to be called when operations end for log purpose
-  simgrid::plugins::Operation::on_end_cb([](const simgrid::plugins::Operation* op) {
-    XBT_INFO("Operation %s finished (%d)", op->get_name().c_str(), op->get_count());
-  });
-
-  // Enqueue two executions for operation exec1
-  exec1->enqueue_execs(2);
-
-  // Start the simulation
-  e.run();
-  return 0;
-}
diff --git a/examples/cpp/operation-simple/s4u-operation-simple.tesh b/examples/cpp/operation-simple/s4u-operation-simple.tesh
deleted file mode 100644 (file)
index c9eee76..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env tesh
-
-$ ${bindir:=.}/s4u-operation-simple ${platfdir}/small_platform.xml
-> [10.194200] [operation_simple/INFO] Operation exec1 finished (1)
-> [11.714617] [operation_simple/INFO] Operation comm finished (1)
-> [20.388399] [operation_simple/INFO] Operation exec1 finished (2)
-> [21.908817] [operation_simple/INFO] Operation comm finished (2)
-> [24.821464] [operation_simple/INFO] Operation exec2 finished (1)
-> [37.928311] [operation_simple/INFO] Operation exec2 finished (2)
diff --git a/examples/cpp/operation-switch-host/s4u-operation-switch-host.tesh b/examples/cpp/operation-switch-host/s4u-operation-switch-host.tesh
deleted file mode 100644 (file)
index 20bff40..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env tesh
-
-$ ${bindir:=.}/s4u-operation-switch-host ${platfdir}/small_platform.xml
-> [1.520418] [operation_switch_host/INFO] Operation comm0 finished (1)
-> [2.873012] [operation_switch_host/INFO] Operation comm0 finished (2)
-> [4.393430] [operation_switch_host/INFO] Operation comm0 finished (3)
-> [5.746025] [operation_switch_host/INFO] Operation comm0 finished (4)
-> [14.627265] [operation_switch_host/INFO] Operation exec1 finished (1)
-> [15.979859] [operation_switch_host/INFO] Operation exec2 finished (1)
-> [16.147682] [operation_switch_host/INFO] Operation comm1 finished (1)
-> [17.332454] [operation_switch_host/INFO] Operation comm2 finished (1)
-> [27.734112] [operation_switch_host/INFO] Operation exec1 finished (2)
-> [29.086707] [operation_switch_host/INFO] Operation exec2 finished (2)
-> [29.254529] [operation_switch_host/INFO] Operation comm1 finished (2)
-> [30.439301] [operation_switch_host/INFO] Operation comm2 finished (2)
\ No newline at end of file
diff --git a/examples/cpp/operation-variable-load/s4u-operation-variable-load.tesh b/examples/cpp/operation-variable-load/s4u-operation-variable-load.tesh
deleted file mode 100644 (file)
index b0d5cec..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env tesh
-
-$ ${bindir:=.}/s4u-operation-variable-load ${platfdir}/small_platform.xml
-> [Tremblay:input:(1) 0.000000] [operation_variable_load/INFO] --- Small load ---
-> [1.520418] [operation_variable_load/INFO] Operation comm finished (1)
-> [14.627265] [operation_variable_load/INFO] Operation exec finished (1)
-> [101.520418] [operation_variable_load/INFO] Operation comm finished (2)
-> [114.627265] [operation_variable_load/INFO] Operation exec finished (2)
-> [201.520418] [operation_variable_load/INFO] Operation comm finished (3)
-> [214.627265] [operation_variable_load/INFO] Operation exec finished (3)
-> [Tremblay:input:(1) 1000.000000] [operation_variable_load/INFO] --- Heavy load ---
-> [1001.520418] [operation_variable_load/INFO] Operation comm finished (4)
-> [1003.040835] [operation_variable_load/INFO] Operation comm finished (5)
-> [1004.561253] [operation_variable_load/INFO] Operation comm finished (6)
-> [1014.627265] [operation_variable_load/INFO] Operation exec finished (4)
-> [1027.734112] [operation_variable_load/INFO] Operation exec finished (5)
-> [1040.840959] [operation_variable_load/INFO] Operation exec finished (6)
diff --git a/examples/cpp/photovoltaic-simple/s4u-photovoltaic-simple.cpp b/examples/cpp/photovoltaic-simple/s4u-photovoltaic-simple.cpp
new file mode 100644 (file)
index 0000000..eb18645
--- /dev/null
@@ -0,0 +1,30 @@
+/* Copyright (c) 2003-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. */
+
+#include "simgrid/plugins/photovoltaic.hpp"
+#include "simgrid/s4u.hpp"
+
+XBT_LOG_NEW_DEFAULT_CATEGORY(photovoltaic_simple, "Messages specific for this s4u example");
+
+static void manager()
+{
+  auto pv_panel = simgrid::s4u::Engine::get_instance()->host_by_name("pv_panel");
+  std::vector<std::pair<double, double>> solar_irradiance = {{1, 10}, {100, 5}, {200, 20}};
+  for (auto [t, s] : solar_irradiance) {
+    simgrid::s4u::this_actor::sleep_until(t);
+    sg_photovoltaic_set_solar_irradiance(pv_panel, s);
+    XBT_INFO("%s power: %f", pv_panel->get_cname(), sg_photovoltaic_get_power(pv_panel));
+  }
+}
+
+int main(int argc, char* argv[])
+{
+  simgrid::s4u::Engine e(&argc, argv);
+  e.load_platform(argv[1]);
+  sg_photovoltaic_plugin_init();
+  simgrid::s4u::Actor::create("manager", e.host_by_name("pv_panel"), manager);
+  e.run();
+  return 0;
+}
diff --git a/examples/cpp/photovoltaic-simple/s4u-photovoltaic-simple.tesh b/examples/cpp/photovoltaic-simple/s4u-photovoltaic-simple.tesh
new file mode 100644 (file)
index 0000000..1146cc8
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env tesh
+
+$ ${bindir:=.}/s4u-photovoltaic-simple ${platfdir}/photovoltaic_platform.xml
+> [pv_panel:manager:(1) 1.000000] [photovoltaic_simple/INFO] pv_panel power: 8.000000
+> [pv_panel:manager:(1) 100.000000] [photovoltaic_simple/INFO] pv_panel power: 4.000000
+> [pv_panel:manager:(1) 200.000000] [photovoltaic_simple/INFO] pv_panel power: 16.000000
index 3d1e75d..8ca73b7 100644 (file)
@@ -7,7 +7,7 @@ $ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-c
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/sleep-set' to 'true'
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'actors' to '2'
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 130 unique states visited; 27 backtracks (209 transition replays, 52 states visited overall)
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 66 unique states visited; 11 backtracks (97 transition replays, 20 states visited overall)
 
 p The stats without checkpoints is:               130 unique states visited; 27 backtracks (308 transition replays, 151 states visited overall)
 p But it runs much faster (0.6 sec vs. 1.6 sec), damn slow checkpointing code.
index 3ef36cc..4de47b1 100644 (file)
@@ -81,11 +81,11 @@ $ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-c
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/sleep-set' to 'true'
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'actors' to '2'
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 130 unique states visited; 27 backtracks (308 transition replays, 151 states visited overall)
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 66 unique states visited; 11 backtracks (126 transition replays, 49 states visited overall)
 
 $ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/strategy:nb_wait -- ${bindir:=.}/s4u-synchro-mutex  --cfg=actors:3 --log=s4u_test.thres:critical
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/sleep-set' to 'true'
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/strategy' to 'nb_wait'
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'actors' to '3'
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 3492 unique states visited; 743 backtracks (12498 transition replays, 8263 states visited overall)
\ No newline at end of file
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 296 unique states visited; 52 backtracks (765 transition replays, 417 states visited overall)
\ No newline at end of file
diff --git a/examples/cpp/task-io/s4u-task-io.cpp b/examples/cpp/task-io/s4u-task-io.cpp
new file mode 100644 (file)
index 0000000..4150f44
--- /dev/null
@@ -0,0 +1,54 @@
+/* Copyright (c) 2017-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. */
+
+/* This example demonstrate basic use of the task plugin.
+ *
+ * We model the following graph:
+ *
+ * exec1 -> comm -> exec2
+ *
+ * exec1 and exec2 are execution tasks.
+ * comm is a communication task.
+ */
+
+#include "simgrid/plugins/task.hpp"
+#include "simgrid/s4u.hpp"
+#include <simgrid/plugins/file_system.h>
+
+XBT_LOG_NEW_DEFAULT_CATEGORY(task_simple, "Messages specific for this task example");
+
+int main(int argc, char* argv[])
+{
+  simgrid::s4u::Engine e(&argc, argv);
+  e.load_platform(argv[1]);
+  simgrid::plugins::Task::init();
+
+  // Retrieve hosts
+  auto bob  = e.host_by_name("bob");
+  auto carl = e.host_by_name("carl");
+
+  // Create tasks
+  auto exec1 = simgrid::plugins::ExecTask::init("exec1", 1e9, bob);
+  auto exec2 = simgrid::plugins::ExecTask::init("exec2", 1e9, carl);
+  auto write = simgrid::plugins::IoTask::init("write", 1e7, bob->get_disks().front(), simgrid::s4u::Io::OpType::WRITE);
+  auto read  = simgrid::plugins::IoTask::init("read", 1e7, carl->get_disks().front(), simgrid::s4u::Io::OpType::READ);
+
+  // Create the graph by defining dependencies between tasks
+  exec1->add_successor(write);
+  write->add_successor(read);
+  read->add_successor(exec2);
+
+  // Add a function to be called when tasks end for log purpose
+  simgrid::plugins::Task::on_end_cb([](const simgrid::plugins::Task* t) {
+    XBT_INFO("Task %s finished (%d)", t->get_name().c_str(), t->get_count());
+  });
+
+  // Enqueue two executions for task exec1
+  exec1->enqueue_execs(2);
+
+  // Start the simulation
+  e.run();
+  return 0;
+}
diff --git a/examples/cpp/task-io/s4u-task-io.tesh b/examples/cpp/task-io/s4u-task-io.tesh
new file mode 100644 (file)
index 0000000..b1b5cdb
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env tesh
+
+$ ${bindir:=.}/s4u-task-io ${platfdir}/hosts_with_disks.xml
+> [1.000000] [task_simple/INFO] Task exec1 finished (1)
+> [1.250000] [task_simple/INFO] Task write finished (1)
+> [1.350000] [task_simple/INFO] Task read finished (1)
+> [2.000000] [task_simple/INFO] Task exec1 finished (2)
+> [2.250000] [task_simple/INFO] Task write finished (2)
+> [2.350000] [task_simple/INFO] Task exec2 finished (1)
+> [2.350000] [task_simple/INFO] Task read finished (2)
+> [3.350000] [task_simple/INFO] Task exec2 finished (2)
diff --git a/examples/cpp/task-simple/s4u-task-simple.cpp b/examples/cpp/task-simple/s4u-task-simple.cpp
new file mode 100644 (file)
index 0000000..0eddb5a
--- /dev/null
@@ -0,0 +1,51 @@
+/* Copyright (c) 2017-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. */
+
+/* This example demonstrate basic use of the task plugin.
+ *
+ * We model the following graph:
+ *
+ * exec1 -> comm -> exec2
+ *
+ * exec1 and exec2 are execution tasks.
+ * comm is a communication task.
+ */
+
+#include "simgrid/plugins/task.hpp"
+#include "simgrid/s4u.hpp"
+
+XBT_LOG_NEW_DEFAULT_CATEGORY(task_simple, "Messages specific for this task example");
+
+int main(int argc, char* argv[])
+{
+  simgrid::s4u::Engine e(&argc, argv);
+  e.load_platform(argv[1]);
+  simgrid::plugins::Task::init();
+
+  // Retrieve hosts
+  auto tremblay = e.host_by_name("Tremblay");
+  auto jupiter  = e.host_by_name("Jupiter");
+
+  // Create tasks
+  auto exec1 = simgrid::plugins::ExecTask::init("exec1", 1e9, tremblay);
+  auto exec2 = simgrid::plugins::ExecTask::init("exec2", 1e9, jupiter);
+  auto comm  = simgrid::plugins::CommTask::init("comm", 1e7, tremblay, jupiter);
+
+  // Create the graph by defining dependencies between tasks
+  exec1->add_successor(comm);
+  comm->add_successor(exec2);
+
+  // Add a function to be called when tasks end for log purpose
+  simgrid::plugins::Task::on_end_cb([](const simgrid::plugins::Task* t) {
+    XBT_INFO("Task %s finished (%d)", t->get_name().c_str(), t->get_count());
+  });
+
+  // Enqueue two executions for task exec1
+  exec1->enqueue_execs(2);
+
+  // Start the simulation
+  e.run();
+  return 0;
+}
diff --git a/examples/cpp/task-simple/s4u-task-simple.tesh b/examples/cpp/task-simple/s4u-task-simple.tesh
new file mode 100644 (file)
index 0000000..b07d406
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/env tesh
+
+$ ${bindir:=.}/s4u-task-simple ${platfdir}/small_platform.xml
+> [10.194200] [task_simple/INFO] Task exec1 finished (1)
+> [11.714617] [task_simple/INFO] Task comm finished (1)
+> [20.388399] [task_simple/INFO] Task exec1 finished (2)
+> [21.908817] [task_simple/INFO] Task comm finished (2)
+> [24.821464] [task_simple/INFO] Task exec2 finished (1)
+> [37.928311] [task_simple/INFO] Task exec2 finished (2)
@@ -3,7 +3,7 @@
 /* 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. */
 
-/* This example demonstrates how to dynamically modify a graph of operations.
+/* This example demonstrates how to dynamically modify a graph of tasks.
  *
  * Assuming we have two instances of a service placed on different hosts,
  * we want to send data alternatively to thoses instances.
  * With exec1 and exec2 on different hosts.
  */
 
-#include "simgrid/plugins/operation.hpp"
+#include "simgrid/plugins/task.hpp"
 #include "simgrid/s4u.hpp"
 
-XBT_LOG_NEW_DEFAULT_CATEGORY(operation_switch_host, "Messages specific for this operation example");
+XBT_LOG_NEW_DEFAULT_CATEGORY(task_switch_host, "Messages specific for this task example");
 
 int main(int argc, char* argv[])
 {
   simgrid::s4u::Engine e(&argc, argv);
   e.load_platform(argv[1]);
-  simgrid::plugins::Operation::init();
+  simgrid::plugins::Task::init();
 
   // Retrieve hosts
   auto* tremblay = e.host_by_name("Tremblay");
   auto* jupiter  = e.host_by_name("Jupiter");
   auto* fafard   = e.host_by_name("Fafard");
 
-  // Create operations
-  auto comm0 = simgrid::plugins::CommOp::init("comm0");
+  // Create tasks
+  auto comm0 = simgrid::plugins::CommTask::init("comm0");
   comm0->set_bytes(1e7);
   comm0->set_source(tremblay);
-  auto exec1 = simgrid::plugins::ExecOp::init("exec1", 1e9, jupiter);
-  auto exec2 = simgrid::plugins::ExecOp::init("exec2", 1e9, fafard);
-  auto comm1 = simgrid::plugins::CommOp::init("comm1", 1e7, jupiter, tremblay);
-  auto comm2 = simgrid::plugins::CommOp::init("comm2", 1e7, fafard, tremblay);
+  auto exec1 = simgrid::plugins::ExecTask::init("exec1", 1e9, jupiter);
+  auto exec2 = simgrid::plugins::ExecTask::init("exec2", 1e9, fafard);
+  auto comm1 = simgrid::plugins::CommTask::init("comm1", 1e7, jupiter, tremblay);
+  auto comm2 = simgrid::plugins::CommTask::init("comm2", 1e7, fafard, tremblay);
 
-  // Create the initial graph by defining dependencies between operations
+  // Create the initial graph by defining dependencies between tasks
   comm0->add_successor(exec2);
   exec1->add_successor(comm1);
   exec2->add_successor(comm2);
 
-  // Add a function to be called when operations end for log purpose
-  simgrid::plugins::Operation::on_end_cb([](const simgrid::plugins::Operation* op) {
-    XBT_INFO("Operation %s finished (%d)", op->get_name().c_str(), op->get_count());
+  // Add a function to be called when tasks end for log purpose
+  simgrid::plugins::Task::on_end_cb([](const simgrid::plugins::Task* t) {
+    XBT_INFO("Task %s finished (%d)", t->get_name().c_str(), t->get_count());
   });
 
   // Add a function to be called before each executions of comm0
-  // This function modifies the graph of operations by adding or removing
+  // This function modifies the graph of tasks by adding or removing
   // successors to comm0
-  comm0->on_this_start([exec1, exec2, jupiter, fafard](simgrid::plugins::Operation* op) {
-    auto* comm0      = dynamic_cast<simgrid::plugins::CommOp*>(op);
+  comm0->on_this_start([exec1, exec2, jupiter, fafard](simgrid::plugins::Task* t) {
+    auto* comm0      = dynamic_cast<simgrid::plugins::CommTask*>(t);
     static int count = 0;
     if (count % 2 == 0) {
       comm0->set_destination(jupiter);
@@ -69,7 +69,7 @@ int main(int argc, char* argv[])
     count++;
   });
 
-  // Enqueue four executions for operation comm0
+  // Enqueue four executions for task comm0
   comm0->enqueue_execs(4);
 
   // Start the simulation
diff --git a/examples/cpp/task-switch-host/s4u-task-switch-host.tesh b/examples/cpp/task-switch-host/s4u-task-switch-host.tesh
new file mode 100644 (file)
index 0000000..b3146ff
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env tesh
+
+$ ${bindir:=.}/s4u-task-switch-host ${platfdir}/small_platform.xml
+> [1.520418] [task_switch_host/INFO] Task comm0 finished (1)
+> [2.873012] [task_switch_host/INFO] Task comm0 finished (2)
+> [4.393430] [task_switch_host/INFO] Task comm0 finished (3)
+> [5.746025] [task_switch_host/INFO] Task comm0 finished (4)
+> [14.627265] [task_switch_host/INFO] Task exec1 finished (1)
+> [15.979859] [task_switch_host/INFO] Task exec2 finished (1)
+> [16.147682] [task_switch_host/INFO] Task comm1 finished (1)
+> [17.332454] [task_switch_host/INFO] Task comm2 finished (1)
+> [27.734112] [task_switch_host/INFO] Task exec1 finished (2)
+> [29.086707] [task_switch_host/INFO] Task exec2 finished (2)
+> [29.254529] [task_switch_host/INFO] Task comm1 finished (2)
+> [30.439301] [task_switch_host/INFO] Task comm2 finished (2)
\ No newline at end of file
@@ -3,32 +3,32 @@
 /* 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. */
 
-/* This example demonstrates how to create a variable load for operations.
+/* This example demonstrates how to create a variable load for tasks.
  *
  * We consider the following graph:
  *
  * comm -> exec
  *
- * With a small load each comm operation is followed by an exec operation.
- * With a heavy load there is a burst of comm before the exec operation can even finish once.
+ * With a small load each comm task is followed by an exec task.
+ * With a heavy load there is a burst of comm before the exec task can even finish once.
  */
 
-#include "simgrid/plugins/operation.hpp"
+#include "simgrid/plugins/task.hpp"
 #include "simgrid/s4u.hpp"
 
-XBT_LOG_NEW_DEFAULT_CATEGORY(operation_variable_load, "Messages specific for this s4u example");
+XBT_LOG_NEW_DEFAULT_CATEGORY(task_variable_load, "Messages specific for this s4u example");
 
-static void variable_load(simgrid::plugins::OperationPtr op)
+static void variable_load(simgrid::plugins::TaskPtr t)
 {
   XBT_INFO("--- Small load ---");
   for (int i = 0; i < 3; i++) {
-    op->enqueue_execs(1);
+    t->enqueue_execs(1);
     simgrid::s4u::this_actor::sleep_for(100);
   }
   simgrid::s4u::this_actor::sleep_until(1000);
   XBT_INFO("--- Heavy load ---");
   for (int i = 0; i < 3; i++) {
-    op->enqueue_execs(1);
+    t->enqueue_execs(1);
     simgrid::s4u::this_actor::sleep_for(1);
   }
 }
@@ -37,22 +37,22 @@ int main(int argc, char* argv[])
 {
   simgrid::s4u::Engine e(&argc, argv);
   e.load_platform(argv[1]);
-  simgrid::plugins::Operation::init();
+  simgrid::plugins::Task::init();
 
   // Retreive hosts
   auto tremblay = e.host_by_name("Tremblay");
   auto jupiter  = e.host_by_name("Jupiter");
 
-  // Create operations
-  auto comm = simgrid::plugins::CommOp::init("comm", 1e7, tremblay, jupiter);
-  auto exec = simgrid::plugins::ExecOp::init("exec", 1e9, jupiter);
+  // Create tasks
+  auto comm = simgrid::plugins::CommTask::init("comm", 1e7, tremblay, jupiter);
+  auto exec = simgrid::plugins::ExecTask::init("exec", 1e9, jupiter);
 
-  // Create the graph by defining dependencies between operations
+  // Create the graph by defining dependencies between tasks
   comm->add_successor(exec);
 
-  // Add a function to be called when operations end for log purpose
-  simgrid::plugins::Operation::on_end_cb([](const simgrid::plugins::Operation* op) {
-    XBT_INFO("Operation %s finished (%d)", op->get_name().c_str(), op->get_count());
+  // Add a function to be called when tasks end for log purpose
+  simgrid::plugins::Task::on_end_cb([](const simgrid::plugins::Task* t) {
+    XBT_INFO("Task %s finished (%d)", t->get_name().c_str(), t->get_count());
   });
 
   // Create the actor that will inject load during the simulation
diff --git a/examples/cpp/task-variable-load/s4u-task-variable-load.tesh b/examples/cpp/task-variable-load/s4u-task-variable-load.tesh
new file mode 100644 (file)
index 0000000..2f2ab9b
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env tesh
+
+$ ${bindir:=.}/s4u-task-variable-load ${platfdir}/small_platform.xml
+> [Tremblay:input:(1) 0.000000] [task_variable_load/INFO] --- Small load ---
+> [1.520418] [task_variable_load/INFO] Task comm finished (1)
+> [14.627265] [task_variable_load/INFO] Task exec finished (1)
+> [101.520418] [task_variable_load/INFO] Task comm finished (2)
+> [114.627265] [task_variable_load/INFO] Task exec finished (2)
+> [201.520418] [task_variable_load/INFO] Task comm finished (3)
+> [214.627265] [task_variable_load/INFO] Task exec finished (3)
+> [Tremblay:input:(1) 1000.000000] [task_variable_load/INFO] --- Heavy load ---
+> [1001.520418] [task_variable_load/INFO] Task comm finished (4)
+> [1003.040835] [task_variable_load/INFO] Task comm finished (5)
+> [1004.561253] [task_variable_load/INFO] Task comm finished (6)
+> [1014.627265] [task_variable_load/INFO] Task exec finished (4)
+> [1027.734112] [task_variable_load/INFO] Task exec finished (5)
+> [1040.840959] [task_variable_load/INFO] Task exec finished (6)
index b6dafe2..99d6865 100644 (file)
@@ -117,253 +117,257 @@ $ tail -n +3 procmig.trace
 > %EndEventDef
 > 0 1 0 HOST
 > 6 0.000000 1 1 0 "Tremblay"
+> 2 2 1 HOST_STATE
+> 5 3 2 receive "1 0 0"
+> 5 4 2 send "0 0 1"
+> 5 5 2 execute "0 1 1"
 > 6 0.000000 2 1 0 "Jupiter"
 > 6 0.000000 3 1 0 "Fafard"
 > 6 0.000000 4 1 0 "Ginette"
 > 6 0.000000 5 1 0 "Bourassa"
 > 6 0.000000 6 1 0 "Jacquelin"
 > 6 0.000000 7 1 0 "Boivin"
-> 0 2 0 LINK
-> 6 0.000000 8 2 0 "6"
-> 6 0.000000 9 2 0 "3"
-> 6 0.000000 10 2 0 "7"
-> 6 0.000000 11 2 0 "9"
-> 6 0.000000 12 2 0 "2"
-> 6 0.000000 13 2 0 "8"
-> 6 0.000000 14 2 0 "1"
-> 6 0.000000 15 2 0 "4"
-> 6 0.000000 16 2 0 "0"
-> 6 0.000000 17 2 0 "5"
-> 6 0.000000 18 2 0 "145"
-> 6 0.000000 19 2 0 "10"
-> 6 0.000000 20 2 0 "11"
-> 6 0.000000 21 2 0 "16"
-> 6 0.000000 22 2 0 "17"
-> 6 0.000000 23 2 0 "44"
-> 6 0.000000 24 2 0 "47"
-> 6 0.000000 25 2 0 "54"
-> 6 0.000000 26 2 0 "56"
-> 6 0.000000 27 2 0 "59"
-> 6 0.000000 28 2 0 "78"
-> 6 0.000000 29 2 0 "79"
-> 6 0.000000 30 2 0 "80"
-> 6 0.000000 31 2 0 "loopback"
-> 4 3 0 2 2 0-LINK2-LINK2
-> 4 4 0 1 2 0-HOST1-LINK2
-> 4 5 0 2 1 0-LINK2-HOST1
-> 15 0.000000 3 0 topology 12 0
-> 16 0.000000 3 0 topology 16 0
-> 15 0.000000 3 0 topology 9 1
-> 16 0.000000 3 0 topology 16 1
-> 15 0.000000 3 0 topology 16 2
-> 16 0.000000 3 0 topology 14 2
-> 15 0.000000 3 0 topology 21 3
-> 16 0.000000 3 0 topology 19 3
-> 15 0.000000 3 0 topology 8 4
-> 16 0.000000 3 0 topology 19 4
-> 15 0.000000 3 0 topology 19 5
-> 16 0.000000 3 0 topology 20 5
-> 15 0.000000 3 0 topology 8 6
-> 16 0.000000 3 0 topology 20 6
-> 15 0.000000 3 0 topology 27 7
-> 16 0.000000 3 0 topology 18 7
-> 15 0.000000 4 0 topology 5 8
-> 16 0.000000 4 0 topology 18 8
-> 15 0.000000 4 0 topology 4 9
-> 16 0.000000 4 0 topology 18 9
-> 15 0.000000 4 0 topology 2 10
-> 16 0.000000 4 0 topology 18 10
-> 15 0.000000 3 0 topology 16 11
-> 16 0.000000 3 0 topology 21 11
-> 15 0.000000 3 0 topology 21 12
-> 16 0.000000 3 0 topology 22 12
-> 15 0.000000 3 0 topology 9 13
-> 16 0.000000 3 0 topology 12 13
-> 15 0.000000 3 0 topology 15 14
-> 16 0.000000 3 0 topology 9 14
-> 15 0.000000 4 0 topology 1 15
-> 16 0.000000 4 0 topology 9 15
-> 15 0.000000 3 0 topology 20 16
-> 16 0.000000 3 0 topology 23 16
-> 15 0.000000 3 0 topology 23 17
-> 16 0.000000 3 0 topology 24 17
-> 15 0.000000 4 0 topology 5 18
-> 16 0.000000 4 0 topology 24 18
-> 15 0.000000 4 0 topology 4 19
-> 16 0.000000 4 0 topology 24 19
-> 15 0.000000 4 0 topology 2 20
-> 16 0.000000 4 0 topology 24 20
-> 15 0.000000 3 0 topology 11 21
-> 16 0.000000 3 0 topology 15 21
-> 15 0.000000 4 0 topology 1 22
-> 16 0.000000 4 0 topology 15 22
-> 15 0.000000 3 0 topology 12 23
-> 16 0.000000 3 0 topology 17 23
-> 15 0.000000 3 0 topology 9 24
-> 16 0.000000 3 0 topology 17 24
-> 15 0.000000 3 0 topology 22 25
-> 16 0.000000 3 0 topology 25 25
-> 15 0.000000 3 0 topology 12 26
-> 16 0.000000 3 0 topology 25 26
-> 15 0.000000 3 0 topology 25 27
-> 16 0.000000 3 0 topology 26 27
-> 15 0.000000 3 0 topology 26 28
-> 16 0.000000 3 0 topology 27 28
-> 15 0.000000 3 0 topology 14 29
-> 16 0.000000 3 0 topology 8 29
-> 15 0.000000 3 0 topology 13 30
-> 16 0.000000 3 0 topology 8 30
-> 15 0.000000 3 0 topology 11 31
-> 16 0.000000 3 0 topology 8 31
-> 15 0.000000 3 0 topology 8 32
-> 16 0.000000 3 0 topology 10 32
-> 15 0.000000 3 0 topology 30 33
-> 16 0.000000 3 0 topology 28 33
-> 15 0.000000 4 0 topology 3 34
-> 16 0.000000 4 0 topology 28 34
-> 15 0.000000 3 0 topology 28 35
-> 16 0.000000 3 0 topology 29 35
-> 15 0.000000 4 0 topology 3 36
-> 16 0.000000 4 0 topology 30 36
-> 15 0.000000 3 0 topology 14 37
-> 16 0.000000 3 0 topology 13 37
-> 15 0.000000 3 0 topology 29 38
-> 16 0.000000 3 0 topology 11 38
-> 15 0.000000 4 0 topology 1 39
-> 16 0.000000 4 0 topology 11 39
-> 15 0.000000 5 0 topology 24 40
-> 16 0.000000 5 0 topology 7 40
-> 15 0.000000 5 0 topology 10 41
-> 16 0.000000 5 0 topology 5 41
-> 15 0.000000 5 0 topology 13 42
-> 16 0.000000 5 0 topology 3 42
-> 15 0.000000 5 0 topology 17 43
-> 16 0.000000 5 0 topology 4 43
-> 15 0.000000 5 0 topology 18 44
-> 16 0.000000 5 0 topology 6 44
-> 15 0.000000 5 0 topology 11 45
-> 16 0.000000 5 0 topology 2 45
-> 0 6 1 ACTOR
-> 6 0.000000 32 6 3 "emigrant-1"
-> 2 7 6 ACTOR_STATE
-> 5 8 7 suspend "1 0 1"
-> 5 9 7 sleep "1 1 0"
-> 5 10 7 receive "1 0 0"
-> 5 11 7 send "0 0 1"
-> 5 12 7 execute "0 1 1"
-> 4 13 0 6 6 ACTOR_LINK
-> 6 0.000000 33 6 1 "policeman-2"
-> 12 0.000000 7 32 9
-> 12 0.000000 7 33 11
-> 13 2.000000 7 32
-> 12 2.000000 7 32 10
-> 13 2.025708 7 33
-> 12 2.025708 7 33 11
-> 13 2.025708 7 32
-> 15 2.025708 13 0 M 32 0
-> 7 2.025708 6 32
-> 6 2.025708 34 6 1 "emigrant-1"
-> 16 2.025708 13 0 M 34 0
-> 12 2.025708 7 34 9
-> 13 4.025708 7 34
-> 12 4.025708 7 34 10
-> 13 4.025903 7 33
-> 12 4.025903 7 33 11
-> 13 4.025903 7 34
-> 15 4.025903 13 0 M 34 1
-> 7 4.025903 6 34
-> 6 4.025903 35 6 2 "emigrant-1"
-> 16 4.025903 13 0 M 35 1
-> 12 4.025903 7 35 9
-> 13 6.025903 7 35
-> 12 6.025903 7 35 10
-> 13 6.044918 7 33
-> 12 6.044918 7 33 11
-> 13 6.044918 7 35
-> 15 6.044918 13 0 M 35 2
-> 7 6.044918 6 35
-> 6 6.044918 36 6 3 "emigrant-1"
-> 16 6.044918 13 0 M 36 2
-> 12 6.044918 7 36 9
-> 13 8.044918 7 36
-> 12 8.044918 7 36 10
-> 13 8.070626 7 33
-> 12 8.070626 7 33 11
-> 13 8.070626 7 36
-> 15 8.070626 13 0 M 36 3
-> 7 8.070626 6 36
-> 6 8.070626 37 6 4 "emigrant-1"
-> 16 8.070626 13 0 M 37 3
-> 12 8.070626 7 37 9
-> 13 10.070626 7 37
-> 12 10.070626 7 37 10
-> 13 10.087178 7 33
-> 12 10.087178 7 33 11
-> 13 10.087178 7 37
-> 15 10.087178 13 0 M 37 4
-> 7 10.087178 6 37
-> 6 10.087178 38 6 5 "emigrant-1"
-> 16 10.087178 13 0 M 38 4
-> 12 10.087178 7 38 9
-> 13 12.087178 7 38
-> 12 12.087178 7 38 10
-> 13 12.112617 7 33
-> 12 12.112617 7 33 11
-> 13 12.112617 7 38
-> 15 12.112617 13 0 M 38 5
-> 7 12.112617 6 38
-> 6 12.112617 39 6 3 "emigrant-1"
-> 16 12.112617 13 0 M 39 5
-> 12 12.112617 7 39 9
-> 13 14.112617 7 39
-> 12 14.112617 7 39 10
-> 13 14.138325 7 33
-> 12 14.138325 7 33 11
-> 13 14.138325 7 39
-> 15 14.138325 13 0 M 39 6
-> 7 14.138325 6 39
-> 6 14.138325 40 6 1 "emigrant-1"
-> 16 14.138325 13 0 M 40 6
-> 12 14.138325 7 40 9
-> 13 16.138325 7 40
-> 12 16.138325 7 40 10
-> 13 16.138521 7 33
-> 12 16.138521 7 33 11
-> 13 16.138521 7 40
-> 15 16.138521 13 0 M 40 7
-> 7 16.138521 6 40
-> 6 16.138521 41 6 4 "emigrant-1"
-> 16 16.138521 13 0 M 41 7
-> 12 16.138521 7 41 9
-> 13 18.138521 7 41
-> 12 18.138521 7 41 10
-> 13 18.155073 7 33
-> 13 18.155073 7 41
-> 7 18.155073 6 33
-> 7 18.155073 6 41
-> 7 18.155073 2 16
-> 7 18.155073 2 14
-> 7 18.155073 2 19
-> 7 18.155073 2 20
-> 7 18.155073 2 18
-> 7 18.155073 2 21
-> 7 18.155073 2 22
-> 7 18.155073 2 12
-> 7 18.155073 2 9
-> 7 18.155073 2 15
-> 7 18.155073 2 23
-> 7 18.155073 2 24
-> 7 18.155073 2 17
-> 7 18.155073 2 25
-> 7 18.155073 2 26
-> 7 18.155073 2 27
-> 7 18.155073 2 8
-> 7 18.155073 2 10
-> 7 18.155073 2 28
-> 7 18.155073 2 29
-> 7 18.155073 2 13
-> 7 18.155073 2 30
-> 7 18.155073 2 11
+> 0 6 0 LINK
+> 6 0.000000 8 6 0 "6"
+> 6 0.000000 9 6 0 "3"
+> 6 0.000000 10 6 0 "7"
+> 6 0.000000 11 6 0 "9"
+> 6 0.000000 12 6 0 "2"
+> 6 0.000000 13 6 0 "8"
+> 6 0.000000 14 6 0 "1"
+> 6 0.000000 15 6 0 "4"
+> 6 0.000000 16 6 0 "0"
+> 6 0.000000 17 6 0 "5"
+> 6 0.000000 18 6 0 "145"
+> 6 0.000000 19 6 0 "10"
+> 6 0.000000 20 6 0 "11"
+> 6 0.000000 21 6 0 "16"
+> 6 0.000000 22 6 0 "17"
+> 6 0.000000 23 6 0 "44"
+> 6 0.000000 24 6 0 "47"
+> 6 0.000000 25 6 0 "54"
+> 6 0.000000 26 6 0 "56"
+> 6 0.000000 27 6 0 "59"
+> 6 0.000000 28 6 0 "78"
+> 6 0.000000 29 6 0 "79"
+> 6 0.000000 30 6 0 "80"
+> 6 0.000000 31 6 0 "loopback"
+> 4 7 0 6 6 0-LINK6-LINK6
+> 4 8 0 1 6 0-HOST1-LINK6
+> 4 9 0 6 1 0-LINK6-HOST1
+> 15 0.000000 7 0 topology 12 0
+> 16 0.000000 7 0 topology 16 0
+> 15 0.000000 7 0 topology 9 1
+> 16 0.000000 7 0 topology 16 1
+> 15 0.000000 7 0 topology 16 2
+> 16 0.000000 7 0 topology 14 2
+> 15 0.000000 7 0 topology 21 3
+> 16 0.000000 7 0 topology 19 3
+> 15 0.000000 7 0 topology 8 4
+> 16 0.000000 7 0 topology 19 4
+> 15 0.000000 7 0 topology 19 5
+> 16 0.000000 7 0 topology 20 5
+> 15 0.000000 7 0 topology 8 6
+> 16 0.000000 7 0 topology 20 6
+> 15 0.000000 7 0 topology 27 7
+> 16 0.000000 7 0 topology 18 7
+> 15 0.000000 8 0 topology 5 8
+> 16 0.000000 8 0 topology 18 8
+> 15 0.000000 8 0 topology 4 9
+> 16 0.000000 8 0 topology 18 9
+> 15 0.000000 8 0 topology 2 10
+> 16 0.000000 8 0 topology 18 10
+> 15 0.000000 7 0 topology 16 11
+> 16 0.000000 7 0 topology 21 11
+> 15 0.000000 7 0 topology 21 12
+> 16 0.000000 7 0 topology 22 12
+> 15 0.000000 7 0 topology 9 13
+> 16 0.000000 7 0 topology 12 13
+> 15 0.000000 7 0 topology 15 14
+> 16 0.000000 7 0 topology 9 14
+> 15 0.000000 8 0 topology 1 15
+> 16 0.000000 8 0 topology 9 15
+> 15 0.000000 7 0 topology 20 16
+> 16 0.000000 7 0 topology 23 16
+> 15 0.000000 7 0 topology 23 17
+> 16 0.000000 7 0 topology 24 17
+> 15 0.000000 8 0 topology 5 18
+> 16 0.000000 8 0 topology 24 18
+> 15 0.000000 8 0 topology 4 19
+> 16 0.000000 8 0 topology 24 19
+> 15 0.000000 8 0 topology 2 20
+> 16 0.000000 8 0 topology 24 20
+> 15 0.000000 7 0 topology 11 21
+> 16 0.000000 7 0 topology 15 21
+> 15 0.000000 8 0 topology 1 22
+> 16 0.000000 8 0 topology 15 22
+> 15 0.000000 7 0 topology 12 23
+> 16 0.000000 7 0 topology 17 23
+> 15 0.000000 7 0 topology 9 24
+> 16 0.000000 7 0 topology 17 24
+> 15 0.000000 7 0 topology 22 25
+> 16 0.000000 7 0 topology 25 25
+> 15 0.000000 7 0 topology 12 26
+> 16 0.000000 7 0 topology 25 26
+> 15 0.000000 7 0 topology 25 27
+> 16 0.000000 7 0 topology 26 27
+> 15 0.000000 7 0 topology 26 28
+> 16 0.000000 7 0 topology 27 28
+> 15 0.000000 7 0 topology 14 29
+> 16 0.000000 7 0 topology 8 29
+> 15 0.000000 7 0 topology 13 30
+> 16 0.000000 7 0 topology 8 30
+> 15 0.000000 7 0 topology 11 31
+> 16 0.000000 7 0 topology 8 31
+> 15 0.000000 7 0 topology 8 32
+> 16 0.000000 7 0 topology 10 32
+> 15 0.000000 7 0 topology 30 33
+> 16 0.000000 7 0 topology 28 33
+> 15 0.000000 8 0 topology 3 34
+> 16 0.000000 8 0 topology 28 34
+> 15 0.000000 7 0 topology 28 35
+> 16 0.000000 7 0 topology 29 35
+> 15 0.000000 8 0 topology 3 36
+> 16 0.000000 8 0 topology 30 36
+> 15 0.000000 7 0 topology 14 37
+> 16 0.000000 7 0 topology 13 37
+> 15 0.000000 7 0 topology 29 38
+> 16 0.000000 7 0 topology 11 38
+> 15 0.000000 8 0 topology 1 39
+> 16 0.000000 8 0 topology 11 39
+> 15 0.000000 9 0 topology 24 40
+> 16 0.000000 9 0 topology 7 40
+> 15 0.000000 9 0 topology 10 41
+> 16 0.000000 9 0 topology 5 41
+> 15 0.000000 9 0 topology 13 42
+> 16 0.000000 9 0 topology 3 42
+> 15 0.000000 9 0 topology 17 43
+> 16 0.000000 9 0 topology 4 43
+> 15 0.000000 9 0 topology 18 44
+> 16 0.000000 9 0 topology 6 44
+> 15 0.000000 9 0 topology 11 45
+> 16 0.000000 9 0 topology 2 45
+> 0 10 1 ACTOR
+> 6 0.000000 32 10 3 "emigrant-1"
+> 2 11 10 ACTOR_STATE
+> 5 12 11 suspend "1 0 1"
+> 5 13 11 sleep "1 1 0"
+> 5 14 11 receive "1 0 0"
+> 5 15 11 send "0 0 1"
+> 5 16 11 execute "0 1 1"
+> 4 17 0 10 10 ACTOR_LINK
+> 6 0.000000 33 10 1 "policeman-2"
+> 12 0.000000 11 32 13
+> 12 0.000000 11 33 15
+> 13 2.000000 11 32
+> 12 2.000000 11 32 14
+> 13 2.025708 11 33
+> 13 2.025708 11 32
+> 12 2.025708 11 33 15
+> 15 2.025708 17 0 M 32 0
+> 7 2.025708 10 32
+> 6 2.025708 34 10 1 "emigrant-1"
+> 16 2.025708 17 0 M 34 0
+> 12 2.025708 11 34 13
+> 13 4.025708 11 34
+> 12 4.025708 11 34 14
+> 13 4.025903 11 33
+> 13 4.025903 11 34
+> 12 4.025903 11 33 15
+> 15 4.025903 17 0 M 34 1
+> 7 4.025903 10 34
+> 6 4.025903 35 10 2 "emigrant-1"
+> 16 4.025903 17 0 M 35 1
+> 12 4.025903 11 35 13
+> 13 6.025903 11 35
+> 12 6.025903 11 35 14
+> 13 6.044918 11 33
+> 13 6.044918 11 35
+> 12 6.044918 11 33 15
+> 15 6.044918 17 0 M 35 2
+> 7 6.044918 10 35
+> 6 6.044918 36 10 3 "emigrant-1"
+> 16 6.044918 17 0 M 36 2
+> 12 6.044918 11 36 13
+> 13 8.044918 11 36
+> 12 8.044918 11 36 14
+> 13 8.070626 11 33
+> 13 8.070626 11 36
+> 12 8.070626 11 33 15
+> 15 8.070626 17 0 M 36 3
+> 7 8.070626 10 36
+> 6 8.070626 37 10 4 "emigrant-1"
+> 16 8.070626 17 0 M 37 3
+> 12 8.070626 11 37 13
+> 13 10.070626 11 37
+> 12 10.070626 11 37 14
+> 13 10.087178 11 33
+> 13 10.087178 11 37
+> 12 10.087178 11 33 15
+> 15 10.087178 17 0 M 37 4
+> 7 10.087178 10 37
+> 6 10.087178 38 10 5 "emigrant-1"
+> 16 10.087178 17 0 M 38 4
+> 12 10.087178 11 38 13
+> 13 12.087178 11 38
+> 12 12.087178 11 38 14
+> 13 12.112617 11 33
+> 13 12.112617 11 38
+> 12 12.112617 11 33 15
+> 15 12.112617 17 0 M 38 5
+> 7 12.112617 10 38
+> 6 12.112617 39 10 3 "emigrant-1"
+> 16 12.112617 17 0 M 39 5
+> 12 12.112617 11 39 13
+> 13 14.112617 11 39
+> 12 14.112617 11 39 14
+> 13 14.138325 11 33
+> 13 14.138325 11 39
+> 12 14.138325 11 33 15
+> 15 14.138325 17 0 M 39 6
+> 7 14.138325 10 39
+> 6 14.138325 40 10 1 "emigrant-1"
+> 16 14.138325 17 0 M 40 6
+> 12 14.138325 11 40 13
+> 13 16.138325 11 40
+> 12 16.138325 11 40 14
+> 13 16.138521 11 33
+> 13 16.138521 11 40
+> 12 16.138521 11 33 15
+> 15 16.138521 17 0 M 40 7
+> 7 16.138521 10 40
+> 6 16.138521 41 10 4 "emigrant-1"
+> 16 16.138521 17 0 M 41 7
+> 12 16.138521 11 41 13
+> 13 18.138521 11 41
+> 12 18.138521 11 41 14
+> 13 18.155073 11 33
+> 13 18.155073 11 41
+> 7 18.155073 10 33
+> 7 18.155073 10 41
+> 7 18.155073 6 16
+> 7 18.155073 6 14
+> 7 18.155073 6 19
+> 7 18.155073 6 20
+> 7 18.155073 6 18
+> 7 18.155073 6 21
+> 7 18.155073 6 22
+> 7 18.155073 6 12
+> 7 18.155073 6 9
+> 7 18.155073 6 15
+> 7 18.155073 6 23
+> 7 18.155073 6 24
+> 7 18.155073 6 17
+> 7 18.155073 6 25
+> 7 18.155073 6 26
+> 7 18.155073 6 27
+> 7 18.155073 6 8
+> 7 18.155073 6 10
+> 7 18.155073 6 28
+> 7 18.155073 6 29
+> 7 18.155073 6 13
+> 7 18.155073 6 30
+> 7 18.155073 6 11
 > 7 18.155073 1 7
 > 7 18.155073 1 5
 > 7 18.155073 1 3
@@ -371,6 +375,6 @@ $ tail -n +3 procmig.trace
 > 7 18.155073 1 6
 > 7 18.155073 1 2
 > 7 18.155073 1 1
-> 7 18.155073 2 31
+> 7 18.155073 6 31
 
 $ rm -f procmig.trace
diff --git a/examples/platforms/photovoltaic_platform.xml b/examples/platforms/photovoltaic_platform.xml
new file mode 100644 (file)
index 0000000..8e499e1
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version='1.0'?>
+<!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
+<platform version="4.1">
+  <zone id="world" routing="Floyd">
+    <host id="pv_panel" speed="0f">
+      <prop id="photovoltaic_area" value="4" />
+      <prop id="photovoltaic_conversion_efficiency" value="0.2" />
+    </host>
+  </zone>
+</platform>
index d2bb58a..f4afe5b 100644 (file)
@@ -3,6 +3,7 @@ foreach(example actor-create actor-daemon actor-join actor-kill actor-migrate ac
         comm-wait comm-waitall comm-waitallfor comm-waitany comm-failure comm-host2host comm-pingpong
         comm-ready comm-suspend comm-testany comm-throttling comm-waitallfor comm-waituntil
         exec-async exec-basic exec-dvfs exec-remote exec-ptask
+        task-io task-simple task-switch-host task-variable-load
         platform-comm-serialize platform-profile platform-failures
         network-nonlinear clusters-multicpu io-degradation exec-cpu-nonlinear
         synchro-barrier synchro-mutex synchro-semaphore)
diff --git a/examples/python/task-io/task-io.py b/examples/python/task-io/task-io.py
new file mode 100644 (file)
index 0000000..18a4cb2
--- /dev/null
@@ -0,0 +1,51 @@
+# Copyright (c) 2006-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
+import sys
+from simgrid import Engine, Task, ExecTask, IoTask, IoOpType
+
+def parse():
+    parser = ArgumentParser()
+    parser.add_argument(
+        '--platform',
+        type=str,
+        required=True,
+        help='path to the platform description'
+    )
+    return parser.parse_args()
+
+def callback( t):
+    print(f'[{Engine.clock}] { t} finished ({ t.count})')
+
+if __name__ == '__main__':
+    args = parse()
+    e = Engine(sys.argv)
+    e.load_platform(args.platform)
+    Task.init()
+
+    # Retrieve hosts
+    bob = e.host_by_name('bob')
+    carl = e.host_by_name('carl')
+
+    # Create tasks
+    exec1 = ExecTask.init("exec1", 1e9, bob)
+    exec2 = ExecTask.init("exec2", 1e9, carl)
+    write = IoTask.init("write", 1e7, bob.disks[0], IoOpType.WRITE)
+    read = IoTask.init("read", 1e7, carl.disks[0], IoOpType.READ)
+
+   # Create the graph by defining dependencies between tasks
+    exec1.add_successor(write)
+    write.add_successor(read)
+    read.add_successor(exec2)
+
+    # Add a function to be called when tasks end for log purpose
+    Task.on_end_cb(callback)
+
+    # Enqueue two executions for task exec1
+    exec1.enqueue_execs(2)
+
+    # runs the simulation
+    e.run()
\ No newline at end of file
diff --git a/examples/python/task-io/task-io.tesh b/examples/python/task-io/task-io.tesh
new file mode 100644 (file)
index 0000000..c6e5691
--- /dev/null
@@ -0,0 +1,12 @@
+#!/usr/bin/env tesh
+
+$ ${pythoncmd:=python3} ${PYTHON_TOOL_OPTIONS:=} ${srcdir:=.}/task-io.py --platform ${platfdir}/hosts_with_disks.xml
+> [1.0] ExecTask(exec1) finished (1)
+> [1.25] IoTask(write) finished (1)
+> [1.35] IoTask(read) finished (1)
+> [2.0] ExecTask(exec1) finished (2)
+> [2.25] IoTask(write) finished (2)
+> [2.35] ExecTask(exec2) finished (1)
+> [2.35] IoTask(read) finished (2)
+> [3.35] ExecTask(exec2) finished (2)
+
diff --git a/examples/python/task-simple/task-simple.py b/examples/python/task-simple/task-simple.py
new file mode 100644 (file)
index 0000000..29a24b7
--- /dev/null
@@ -0,0 +1,60 @@
+# Copyright (c) 2006-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.
+
+"""
+This example demonstrates basic use of the task plugin.
+We model the following graph:
+
+exec1 -> comm -> exec2
+
+exec1 and exec2 are execution tasks.
+comm is a communication task.
+"""
+
+from argparse import ArgumentParser
+import sys
+from simgrid import Engine, Task, CommTask, ExecTask
+
+def parse():
+    parser = ArgumentParser()
+    parser.add_argument(
+        '--platform',
+        type=str,
+        required=True,
+        help='path to the platform description'
+    )
+    return parser.parse_args()
+
+def callback( t):
+    print(f'[{Engine.clock}] { t} finished ({ t.count})')
+
+if __name__ == '__main__':
+    args = parse()
+    e = Engine(sys.argv)
+    e.load_platform(args.platform)
+    Task.init()
+
+    # Retrieve hosts
+    tremblay = e.host_by_name('Tremblay')
+    jupiter = e.host_by_name('Jupiter')
+
+    # Create tasks
+    exec1 = ExecTask.init("exec1", 1e9, tremblay)
+    exec2 = ExecTask.init("exec2", 1e9, jupiter)
+    comm = CommTask.init("comm", 1e7, tremblay, jupiter)
+
+    # Create the graph by defining dependencies between tasks
+    exec1.add_successor(comm)
+    comm.add_successor(exec2)
+
+    # Add a function to be called when tasks end for log purpose
+    Task.on_end_cb(callback)
+
+    # Enqueue two executions for task exec1
+    exec1.enqueue_execs(2)
+
+    # runs the simulation
+    e.run()
+
diff --git a/examples/python/task-simple/task-simple.tesh b/examples/python/task-simple/task-simple.tesh
new file mode 100644 (file)
index 0000000..5a27a53
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/env tesh
+
+$ ${pythoncmd:=python3} ${PYTHON_TOOL_OPTIONS:=} ${srcdir:=.}/task-simple.py --platform ${platfdir}/small_platform.xml
+> [10.194199500484224] ExecTask(exec1) finished (1)
+> [11.714617112501687] CommTask(comm) finished (1)
+> [20.388399000968448] ExecTask(exec1) finished (2)
+> [21.90881661298591] CommTask(comm) finished (2)
+> [24.82146412938331] ExecTask(exec2) finished (1)
+> [37.92831114626493] ExecTask(exec2) finished (2)
diff --git a/examples/python/task-switch-host/task-switch-host.py b/examples/python/task-switch-host/task-switch-host.py
new file mode 100644 (file)
index 0000000..f8d8dfa
--- /dev/null
@@ -0,0 +1,90 @@
+# Copyright (c) 2006-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.
+
+"""
+/* This example demonstrates how to dynamically modify a graph of tasks.
+ *
+ * Assuming we have two instances of a service placed on different hosts,
+ * we want to send data alternatively to thoses instances.
+ *
+ * We consider the following graph:
+
+           comm1
+     ┌────────────────────────┐
+     │                        │
+     │               Fafard   │
+     │              ┌───────┐ │
+     │      ┌──────►│ exec1 ├─┘
+     ▼      │       └───────┘
+ Tremblay ──┤comm0
+     ▲      │        Jupiter
+     │      │       ┌───────┐
+     │      └──────►│ exec2 ├─┐
+     │              └───────┘ │
+     │                        │
+     └────────────────────────┘
+           comm2
+ */
+ """
+
+from argparse import ArgumentParser
+import sys
+from simgrid import Engine, Task, CommTask, ExecTask
+
+def parse():
+    parser = ArgumentParser()
+    parser.add_argument(
+        '--platform',
+        type=str,
+        required=True,
+        help='path to the platform description'
+    )
+    return parser.parse_args()
+
+def callback( t):
+    print(f'[{Engine.clock}] { t} finished ({ t.count})')
+
+def switch( t, hosts, execs):
+    comm0.destination = hosts[ t.count % 2]
+    comm0.remove_successor(execs[ t.count % 2 - 1])
+    comm0.add_successor(execs[ t.count % 2])
+
+if __name__ == '__main__':
+    args = parse()
+    e = Engine(sys.argv)
+    e.load_platform(args.platform)
+    Task.init()
+
+    # Retrieve hosts
+    tremblay = e.host_by_name('Tremblay')
+    jupiter = e.host_by_name('Jupiter')
+    fafard = e.host_by_name('Fafard')
+
+    # Create tasks
+    comm0 = CommTask.init("comm0")
+    comm0.bytes = 1e7
+    comm0.source = tremblay
+    exec1 = ExecTask.init("exec1", 1e9, jupiter)
+    exec2 = ExecTask.init("exec2", 1e9, fafard)
+    comm1 = CommTask.init("comm1", 1e7, jupiter, tremblay)
+    comm2 = CommTask.init("comm2", 1e7, fafard, tremblay)
+
+    # Create the initial graph by defining dependencies between tasks
+    exec1.add_successor(comm1)
+    exec2.add_successor(comm2)
+
+    # Add a function to be called when tasks end for log purpose
+    Task.on_end_cb(callback)
+
+    # Add a function to be called before each executions of comm0
+    # This function modifies the graph of tasks by adding or removing
+    # successors to comm0
+    comm0.on_this_start(lambda  t: switch( t, [jupiter, fafard], [exec1,exec2]))
+
+    # Enqueue two executions for task exec1
+    comm0.enqueue_execs(4)
+
+    # runs the simulation
+    e.run()
diff --git a/examples/python/task-switch-host/task-switch-host.tesh b/examples/python/task-switch-host/task-switch-host.tesh
new file mode 100644 (file)
index 0000000..e506de7
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env tesh
+
+$ ${pythoncmd:=python3} ${PYTHON_TOOL_OPTIONS:=} ${srcdir:=.}/task-switch-host.py --platform ${platfdir}/small_platform.xml
+> [1.5204176120174615] CommTask(comm0) finished (1)
+> [2.873012467069035] CommTask(comm0) finished (2)
+> [4.393430079086497] CommTask(comm0) finished (3)
+> [5.74602493413807] CommTask(comm0) finished (4)
+> [14.62726462889908] ExecTask(exec1) finished (1)
+> [15.979859483950655] ExecTask(exec2) finished (1)
+> [16.14768224091654] CommTask(comm1) finished (1)
+> [17.33245433900223] CommTask(comm2) finished (1)
+> [27.7341116457807] ExecTask(exec1) finished (2)
+> [29.086706500832275] ExecTask(exec2) finished (2)
+> [29.25452925779816] CommTask(comm1) finished (2)
+> [30.43930135588385] CommTask(comm2) finished (2)
diff --git a/examples/python/task-variable-load/task-variable-load.py b/examples/python/task-variable-load/task-variable-load.py
new file mode 100644 (file)
index 0000000..5e437e3
--- /dev/null
@@ -0,0 +1,68 @@
+# Copyright (c) 2006-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.
+
+"""
+This example demonstrates how to create a variable load for tasks.
+We consider the following graph:
+
+comm -> exec
+
+With a small load each comm task is followed by an exec task.
+With a heavy load there is a burst of comm before the exec task can even finish once.
+"""
+
+from argparse import ArgumentParser
+import sys
+from simgrid import Engine, Task, CommTask, ExecTask, Actor, this_actor
+
+def parse():
+    parser = ArgumentParser()
+    parser.add_argument(
+        '--platform',
+        type=str,
+        required=True,
+        help='path to the platform description'
+    )
+    return parser.parse_args()
+
+def callback( t):
+    print(f'[{Engine.clock}] { t} finished ({ t.count})')
+
+def variable_load( t):
+    print('--- Small load ---')
+    for i in range(3):
+        t.enqueue_execs(1)
+        this_actor.sleep_for(100)
+    this_actor.sleep_for(1000)
+    print('--- Heavy load ---')
+    for i in range(3):
+        t.enqueue_execs(1)
+        this_actor.sleep_for(1)
+
+if __name__ == '__main__':
+    args = parse()
+    e = Engine(sys.argv)
+    e.load_platform(args.platform)
+    Task.init()
+
+    # Retrieve hosts
+    tremblay = e.host_by_name('Tremblay')
+    jupiter = e.host_by_name('Jupiter')
+
+    # Create tasks
+    comm = CommTask.init("comm", 1e7, tremblay, jupiter)
+    exec = ExecTask.init("exec", 1e9, jupiter)
+
+    # Create the graph by defining dependencies between tasks
+    comm.add_successor(exec)
+
+    # Add a function to be called when tasks end for log purpose
+    Task.on_end_cb(callback)
+
+    # Create the actor that will inject load during the simulation
+    Actor.create("input", tremblay, variable_load, comm)
+
+    # runs the simulation
+    e.run()
diff --git a/examples/python/task-variable-load/task-variable-load.tesh b/examples/python/task-variable-load/task-variable-load.tesh
new file mode 100644 (file)
index 0000000..0aa185a
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/env tesh
+
+$ ${pythoncmd:=python3} ${PYTHON_TOOL_OPTIONS:=} ${srcdir:=.}/task-variable-load.py --platform ${platfdir}/small_platform.xml
+> --- Small load ---
+> [1.5204176120174615] CommTask(comm) finished (1)
+> [14.62726462889908] ExecTask(exec) finished (1)
+> [101.52041761201747] CommTask(comm) finished (2)
+> [114.62726462889908] ExecTask(exec) finished (2)
+> [201.52041761201744] CommTask(comm) finished (3)
+> [214.62726462889907] ExecTask(exec) finished (3)
+> --- Heavy load ---
+> [1301.5204176120174] CommTask(comm) finished (4)
+> [1303.0408352240347] CommTask(comm) finished (5)
+> [1304.561252836052] CommTask(comm) finished (6)
+> [1314.627264628899] ExecTask(exec) finished (4)
+> [1327.7341116457806] ExecTask(exec) finished (5)
+> [1340.8409586626622] ExecTask(exec) finished (6)
+
+
index d6d474a..3946c2d 100644 (file)
@@ -9,10 +9,10 @@ $ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-chec
 > The thread 0 is terminating.
 > The thread 1 is terminating.
 > User's main is terminating.
-> The thread 1 is terminating.
 > The thread 0 is terminating.
+> The thread 1 is terminating.
 > User's main is terminating.
-> The thread 0 is terminating.
 > The thread 1 is terminating.
+> The thread 0 is terminating.
 > User's main is terminating.
 > [0.000000] [mc_dfs/INFO] DFS exploration ended. 23 unique states visited; 2 backtracks (27 transition replays, 2 states visited overall)
\ No newline at end of file
index 666325f..590fdc6 100644 (file)
@@ -12,6 +12,9 @@ $ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-chec
 > The thread 0 is terminating.
 > The thread 1 is terminating.
 > User's main is terminating.
+> The thread 0 is terminating.
+> The thread 1 is terminating.
+> User's main is terminating.
 > [0.000000] [mc_global/INFO] **************************
 > [0.000000] [mc_global/INFO] *** DEADLOCK DETECTED ***
 > [0.000000] [mc_global/INFO] **************************
@@ -28,4 +31,4 @@ $ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-chec
 > [0.000000] [mc_global/INFO]   3: MUTEX_WAIT(mutex: 1, owner: 3)
 > [0.000000] [mc_global/INFO]   3: MUTEX_ASYNC_LOCK(mutex: 0, owner: 2)
 > [0.000000] [mc_Session/INFO] You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'2;2;3;2;3;3'
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 19 unique states visited; 1 backtracks (22 transition replays, 2 states visited overall)
\ No newline at end of file
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 28 unique states visited; 2 backtracks (37 transition replays, 7 states visited overall)
\ No newline at end of file
index 404282a..89be825 100644 (file)
@@ -5,11 +5,11 @@ $ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-chec
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/sleep-set' to 'true'
 > [0.000000] [sthread/INFO] Starting the simulation.
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1101 unique states visited; 136 backtracks (2950 transition replays, 1713 states visited overall)
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 106 unique states visited; 17 backtracks (295 transition replays, 172 states visited overall)
 
 $ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/strategy:nb_wait --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-producer-consumer -q -c 2 -C 1 -p 2 -P 1
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/sleep-set' to 'true'
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/strategy' to 'nb_wait'
 > [0.000000] [sthread/INFO] Starting the simulation.
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1101 unique states visited; 136 backtracks (2950 transition replays, 1713 states visited overall)
\ No newline at end of file
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 107 unique states visited; 18 backtracks (300 transition replays, 175 states visited overall)
\ No newline at end of file
index dae9277..d21a7fa 100644 (file)
@@ -178,6 +178,7 @@ class System;
 }
 namespace resource {
 class Action;
+class CpuAction;
 class CpuImpl;
 class Model;
 class Resource;
index 7fee147..f8cd62d 100644 (file)
@@ -13,8 +13,7 @@
 #include <set>
 #include <string>
 
-namespace simgrid {
-namespace instr {
+namespace simgrid::instr {
 /* User-variables related functions*/
 /* for host variables */
 XBT_PUBLIC void declare_host_variable(const std::string& variable, const std::string& color = "");
@@ -67,8 +66,7 @@ XBT_PUBLIC const std::set<std::string, std::less<>>& get_tracing_categories();
 XBT_PUBLIC void platform_graph_export_graphviz(const std::string& output_filename);
 /* Function used by graphicator (transform a SimGrid platform file in a CSV file with the network topology) */
 XBT_PUBLIC void platform_graph_export_csv(const std::string& output_filename);
-} // namespace instr
-} // namespace simgrid
+} // namespace simgrid::instr
 
 #endif
 SG_BEGIN_DECL
index 8e72a2d..8ec4f7c 100644 (file)
@@ -9,10 +9,7 @@
 #include <simgrid/forward.h>
 #include <functional>
 
-namespace simgrid {
-namespace kernel {
-namespace profile {
-
+namespace simgrid::kernel::profile {
 
 /** @brief Modeling of the availability profile (due to an external load) or the churn
  *
@@ -75,8 +72,6 @@ public:
   static Profile* from_callback(const std::string& name, const std::function<UpdateCb>& cb, double repeat_delay);
 };
 
-} // namespace profile
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::profile
 
 #endif /* SIMGRID_KERNEL_PROFILEBUILDER_HPP */
index 3a98068..010a66f 100644 (file)
@@ -12,9 +12,7 @@
 
 #include <boost/heap/fibonacci_heap.hpp>
 
-namespace simgrid {
-namespace kernel {
-namespace timer {
+namespace simgrid::kernel::timer {
 
 inline auto& kernel_timers() // avoid static initialization order fiasco
 {
@@ -48,8 +46,6 @@ public:
   static bool execute_all();
 };
 
-} // namespace timer
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::timer
 
 #endif /* SRC_KERNEL_TIMER_TIMER_HPP_ */
index 613ae78..b75750e 100644 (file)
 #include <boost/heap/pairing_heap.hpp>
 #include <boost/optional.hpp>
 #include <string>
+#include <string_view>
 
 static constexpr double NO_MAX_DURATION = -1.0;
 
-namespace simgrid {
-namespace kernel {
-namespace resource {
+namespace simgrid::kernel::resource {
 
 using heap_element_type = std::pair<double, Action*>;
 using heap_type =
@@ -227,7 +226,7 @@ public:
   /** @brief Get the tracing category associated to the current action */
   const std::string& get_category() const { return category_; }
   /** @brief Set the tracing category of the current Action */
-  void set_category(const std::string& category) { category_ = category; }
+  void set_category(std::string_view category) { category_ = category; }
 
   /** @brief Get the sharing_penalty (RTT or 1/thread_count) of the current Action */
   double get_sharing_penalty() const { return sharing_penalty_; };
@@ -273,7 +272,5 @@ public:
   void set_suspend_state(Action::SuspendStates state) { suspended_ = state; }
 };
 
-} // namespace resource
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::resource
 #endif
index 87f8eee..3a5affb 100644 (file)
@@ -10,9 +10,7 @@
 #include <simgrid/kernel/resource/Action.hpp>
 #include <unordered_map>
 
-namespace simgrid {
-namespace kernel {
-namespace resource {
+namespace simgrid::kernel::resource {
 
 class XBT_PUBLIC Model {
 public:
@@ -110,8 +108,6 @@ private:
   ActionHeap action_heap_;
 };
 
-} // namespace resource
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::resource
 
 #endif
index b08bba7..8fc4512 100644 (file)
@@ -11,9 +11,7 @@
 
 #include <unordered_map>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 /**
  * @brief Placeholder for old ClusterZone class
@@ -150,8 +148,6 @@ public:
     return node_pos_with_loopback(id) + (has_limiter_ ? 1 : 0);
   }
 };
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 
 #endif /* SIMGRID_ROUTING_CLUSTER_HPP_ */
index fa4c176..dbaef58 100644 (file)
@@ -8,9 +8,7 @@
 
 #include <simgrid/kernel/routing/RoutedZone.hpp>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 /** @ingroup ROUTING_API
  *  @brief NetZone with an explicit routing computed on need with Dijkstra
@@ -53,8 +51,6 @@ public:
   void add_route(NetPoint* src, NetPoint* dst, NetPoint* gw_src, NetPoint* gw_dst,
                  const std::vector<s4u::LinkInRoute>& link_list, bool symmetrical) override;
 };
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 
 #endif /* SIMGRID_ROUTING_DIJKSTRA_HPP_ */
index 558af8d..acb1122 100644 (file)
@@ -9,9 +9,7 @@
 #include <simgrid/kernel/routing/ClusterZone.hpp>
 #include <simgrid/s4u/Link.hpp>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 class DragonflyRouter {
 public:
@@ -109,7 +107,5 @@ private:
   unsigned int num_links_per_link_     = 1; // splitduplex -> 2, only for local link
   std::vector<DragonflyRouter> routers_;
 };
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 #endif
index 7240493..f5fd334 100644 (file)
@@ -9,9 +9,7 @@
 #include <simgrid/kernel/routing/NetZoneImpl.hpp>
 #include <xbt/asserts.h>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 /** @ingroup ROUTING_API
  *  @brief NetZone with no routing, useful with the constant network model
@@ -32,8 +30,6 @@ public:
   void get_graph(const s_xbt_graph_t* graph, std::map<std::string, xbt_node_t, std::less<>>* /*nodes*/,
                  std::map<std::string, xbt_edge_t, std::less<>>* /*edges*/) override;
 };
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 
 #endif /* SIMGRID_ROUTING_NONE_HPP_ */
index a0623a2..fabeea2 100644 (file)
@@ -8,9 +8,7 @@
 
 #include <simgrid/kernel/routing/ClusterZone.hpp>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 class XBT_PRIVATE FatTreeLink;
 
@@ -159,8 +157,6 @@ public:
   void build_upper_levels(const s4u::ClusterCallbacks& set_callbacks);
   void generate_dot_file(const std::string& filename = "fat_tree.dot") const;
 };
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 
 #endif
index a384746..6cfaa76 100644 (file)
@@ -8,9 +8,7 @@
 
 #include <simgrid/kernel/routing/RoutedZone.hpp>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 /** @ingroup ROUTING_API
  *  @brief NetZone with an explicit routing computed at initialization with Floyd-Warshal
@@ -39,8 +37,6 @@ public:
   void add_route(NetPoint* src, NetPoint* dst, NetPoint* gw_src, NetPoint* gw_dst,
                  const std::vector<s4u::LinkInRoute>& link_list, bool symmetrical) override;
 };
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 
 #endif /* SIMGRID_ROUTING_FLOYD_HPP_ */
index 5e34451..3a83895 100644 (file)
@@ -8,9 +8,7 @@
 
 #include <simgrid/kernel/routing/RoutedZone.hpp>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 /** @ingroup ROUTING_API
  *  @brief NetZone with an explicit routing provided by the user
@@ -33,8 +31,6 @@ public:
   void add_route(NetPoint* src, NetPoint* dst, NetPoint* gw_src, NetPoint* gw_dst,
                  const std::vector<s4u::LinkInRoute>& link_list, bool symmetrical) override;
 };
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 
 #endif /* SIMGRID_ROUTING_FULL_HPP_ */
index 566eee3..aca2452 100644 (file)
@@ -16,8 +16,7 @@ namespace simgrid {
 
 extern template class XBT_PUBLIC xbt::Extendable<kernel::routing::NetPoint>;
 
-namespace kernel {
-namespace routing {
+namespace kernel::routing {
 
 /** @ingroup ROUTING_API
  *  @brief Network cards are the vertices in the graph representing the network, used to compute paths between nodes.
@@ -54,8 +53,7 @@ private:
   NetPoint::Type component_type_;
   NetZoneImpl* englobing_zone_ = nullptr;
 };
-} // namespace routing
-} // namespace kernel
+} // namespace kernel::routing
 } // namespace simgrid
 
 XBT_PUBLIC simgrid::kernel::routing::NetPoint* sg_netpoint_by_name_or_null(const char* name);
index b7d16fb..ffb4322 100644 (file)
@@ -16,9 +16,7 @@
 #include <unordered_set>
 #include <vector>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 class Route {
 public:
@@ -287,8 +285,6 @@ private:
   virtual resource::StandardLinkImpl* do_create_link(const std::string& name, const std::vector<double>& bandwidths);
   void add_child(NetZoneImpl* new_zone);
 };
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 
 #endif /* SIMGRID_ROUTING_NETZONEIMPL_HPP */
index a4e9b67..cf96ee0 100644 (file)
@@ -8,9 +8,7 @@
 
 #include <simgrid/kernel/routing/NetZoneImpl.hpp>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 /** @ingroup ROUTING_API
  *  @brief NetZone with an explicit routing (abstract class)
@@ -59,8 +57,6 @@ protected:
   void add_route_check_params(NetPoint* src, NetPoint* dst, NetPoint* gw_src, NetPoint* gw_dst,
                               const std::vector<s4u::LinkInRoute>& link_list, bool symmetrical) const;
 };
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 
 #endif /* SIMGRID_ROUTING_GENERIC_HPP_ */
index dcf740c..dc533e0 100644 (file)
@@ -11,9 +11,7 @@
 #include <unordered_map>
 #include <unordered_set>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 /** @ingroup ROUTING_API
  *  @brief NetZone where components are connected following a star topology
@@ -93,8 +91,6 @@ private:
                              bool symmetrical) const;
   std::unordered_map<unsigned long, StarRoute> routes_;
 };
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 
 #endif /* SIMGRID_KERNEL_ROUTING_STARZONE_HPP_ */
index af661d3..4eeed3e 100644 (file)
@@ -10,9 +10,7 @@
 
 #include <vector>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 /** @ingroup ROUTING_API
  * @brief NetZone using a Torus topology
@@ -32,7 +30,5 @@ public:
   static std::vector<unsigned long> parse_topo_parameters(const std::string& topo_parameters);
 };
 
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 #endif
index 0af8f0e..c64353a 100644 (file)
@@ -9,9 +9,7 @@
 #include <simgrid/kernel/routing/StarZone.hpp>
 #include <xbt/Extendable.hpp>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 /** @ingroup ROUTING_API
  *  @brief NetZone modeling peers connected to the cloud through a private link
@@ -60,8 +58,6 @@ public:
   std::vector<double> coords;
 };
 } // namespace vivaldi
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 
 #endif /* SIMGRID_ROUTING_VIVALDI_HPP_ */
index 2f25143..d2c6108 100644 (file)
@@ -8,9 +8,7 @@
 
 #include <simgrid/kernel/routing/RoutedZone.hpp>
 
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
 
 /** @ingroup ROUTING_API
  *  @brief NetZone modeling a Wifi zone
@@ -33,8 +31,6 @@ public:
   void get_local_route(const NetPoint* src, const NetPoint* dst, Route* into, double* latency) override;
   NetPoint* get_access_point() const { return access_point_; }
 };
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
 
 #endif /* SIMGRID_ROUTING_WIFI_HPP_ */
index b6af5a4..d4438e4 100644 (file)
@@ -22,8 +22,7 @@ XBT_LOG_EXTERNAL_CATEGORY(producer_consumer);
 
 /** Stock implementation of a generic monitored queue to solve the producer-consumer problem */
 
-namespace simgrid {
-namespace plugin {
+namespace simgrid::plugin {
 
 template <typename T> class ProducerConsumer;
 template <typename T> using ProducerConsumerPtr = boost::intrusive_ptr<ProducerConsumer<T>>;
@@ -104,7 +103,7 @@ public:
    */
   ProducerConsumer* set_max_queue_size(unsigned int max_queue_size)
   {
-    const std::lock_guard<s4u::Mutex> lock(*mutex_);
+    const std::scoped_lock lock(*mutex_);
     max_queue_size_ = max_queue_size;
     return this;
   }
@@ -141,7 +140,7 @@ public:
    */
   s4u::CommPtr put_async(T* data, size_t simulated_size_in_bytes)
   {
-    std::unique_lock<s4u::Mutex> lock(*mutex_);
+    std::unique_lock lock(*mutex_);
     s4u::CommPtr comm = nullptr;
     XBT_CVERB(producer_consumer, (size() < max_queue_size_) ? "can put" : "must wait");
 
@@ -178,7 +177,7 @@ public:
    */
   s4u::CommPtr get_async(T** data)
   {
-    std::unique_lock<s4u::Mutex> lock(*mutex_);
+    std::unique_lock lock(*mutex_);
     s4u::CommPtr comm = nullptr;
     XBT_CVERB(producer_consumer, empty() ? "must wait" : "can get");
     while (empty())
@@ -214,7 +213,6 @@ public:
   }
 };
 
-} // namespace plugin
-} // namespace simgrid
+} // namespace simgrid::plugin
 
 #endif // SIMGRID_PLUGIN_PRODUCERCONSUMER_HPP
diff --git a/include/simgrid/plugins/operation.hpp b/include/simgrid/plugins/operation.hpp
deleted file mode 100644 (file)
index 303b0d1..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-#ifndef SIMGRID_PLUGINS_OPERATION_H_
-#define SIMGRID_PLUGINS_OPERATION_H_
-
-#include <simgrid/s4u/Activity.hpp>
-#include <xbt/Extendable.hpp>
-
-#include <atomic>
-#include <map>
-#include <memory>
-#include <set>
-
-namespace simgrid::plugins {
-
-class Operation;
-using OperationPtr = boost::intrusive_ptr<Operation>;
-XBT_PUBLIC void intrusive_ptr_release(Operation* o);
-XBT_PUBLIC void intrusive_ptr_add_ref(Operation* o);
-class ExecOp;
-using ExecOpPtr = boost::intrusive_ptr<ExecOp>;
-XBT_PUBLIC void intrusive_ptr_release(ExecOp* e);
-XBT_PUBLIC void intrusive_ptr_add_ref(ExecOp* e);
-class CommOp;
-using CommOpPtr =  boost::intrusive_ptr<CommOp>;
-XBT_PUBLIC void intrusive_ptr_release(CommOp* c);
-XBT_PUBLIC void intrusive_ptr_add_ref(CommOp* c);
-
-struct ExtendedAttributeActivity {
-  static simgrid::xbt::Extension<simgrid::s4u::Activity, ExtendedAttributeActivity> EXTENSION_ID;
-  Operation* operation_;
-};
-
-class Operation {
-private:
-  static bool inited_;
-  std::set<Operation*> successors_                 = {};
-  std::map<Operation*, unsigned int> predecessors_ = {};
-
-  void add_predecessor(Operation* predecessor);
-  void remove_predecessor(Operation* predecessor);
-  bool ready_to_run() const;
-  void receive(Operation* source);
-  void complete();
-
-protected:
-  std::string name_;
-  double amount_;
-  int queued_execs_ = 0;
-  int count_        = 0;
-  bool working_     = false;
-  s4u::ActivityPtr current_activity_;
-  std::function<void(Operation*)> end_func_;
-  std::function<void(Operation*)> start_func_;
-  explicit Operation(const std::string& name);
-  virtual ~Operation()   = default;
-  virtual void execute() = 0;
-
-  static xbt::signal<void(Operation*)> on_start;
-  static xbt::signal<void(Operation*)> on_end;
-  std::atomic_int_fast32_t refcount_{0};
-
-public:
-  static void init();
-  const std::string& get_name() const { return name_; }
-  const char* get_cname() const { return name_.c_str(); }
-  void enqueue_execs(int n);
-  void set_amount(double amount);
-  double get_amount() const { return amount_; }
-  void add_successor(OperationPtr op);
-  void remove_successor(OperationPtr op);
-  void remove_all_successors();
-  const std::set<Operation*>& get_successors() const { return successors_ ;}
-  void on_this_start(const std::function<void(Operation*)>& func);
-  void on_this_end(const std::function<void(Operation*)>& func);
-  int get_count() const;
-
-  /** Add a callback fired before an operation activity start.
-   * Triggered after the on_this_start function**/
-  static void on_start_cb(const std::function<void(Operation*)>& cb) { on_start.connect(cb); }
-  /** Add a callback fired after an operation activity end.
-   * Triggered after the on_this_end function, but before
-   * sending tokens to successors.**/
-  static void on_end_cb(const std::function<void(Operation*)>& cb) { on_end.connect(cb); }
-
-#ifndef DOXYGEN
-  friend void intrusive_ptr_release(Operation* o)
-  {
-    if (o->refcount_.fetch_sub(1, std::memory_order_release) == 1) {
-      std::atomic_thread_fence(std::memory_order_acquire);
-      delete o;
-    }
-  }
-  friend void intrusive_ptr_add_ref(Operation* o) { o->refcount_.fetch_add(1, std::memory_order_relaxed); }
-#endif
-};
-
-class ExecOp : public Operation {
-private:
-  s4u::Host* host_;
-
-  explicit ExecOp(const std::string& name);
-  void execute() override;
-
-public:
-  static ExecOpPtr init(const std::string& name);
-  static ExecOpPtr init(const std::string& name, double flops, s4u::Host* host);
-  ExecOpPtr set_host(s4u::Host* host);
-  s4u::Host* get_host() const { return host_; }
-  ExecOpPtr set_flops(double flops);
-  double get_flops() const { return get_amount(); }
-  friend void inline intrusive_ptr_release(ExecOp* e) { intrusive_ptr_release(static_cast<Operation*>(e)); }
-  friend void inline intrusive_ptr_add_ref(ExecOp* e) { intrusive_ptr_add_ref(static_cast<Operation*>(e)); }
-};
-
-class CommOp : public Operation {
-private:
-  s4u::Host* source_;
-  s4u::Host* destination_;
-
-  explicit CommOp(const std::string& name);
-  void execute() override;
-
-public:
-  static CommOpPtr init(const std::string& name);
-  static CommOpPtr init(const std::string& name, double bytes, s4u::Host* source,
-                        s4u::Host* destination);
-  CommOpPtr set_source(s4u::Host* source);
-  s4u::Host* get_source() const { return source_; }
-  CommOpPtr set_destination(s4u::Host* destination);
-  s4u::Host* get_destination() const { return destination_; }
-  CommOpPtr set_bytes(double bytes);
-  double get_bytes() const { return get_amount(); }
-  friend void inline intrusive_ptr_release(CommOp* c) { intrusive_ptr_release(static_cast<Operation*>(c)); }
-  friend void inline intrusive_ptr_add_ref(CommOp* c) { intrusive_ptr_add_ref(static_cast<Operation*>(c)); }
-};
-} // namespace simgrid::plugins
-#endif
diff --git a/include/simgrid/plugins/photovoltaic.hpp b/include/simgrid/plugins/photovoltaic.hpp
new file mode 100644 (file)
index 0000000..7cc1f3b
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef SIMGRID_PLUGINS_PHOTOVOLTAIC_H_
+#define SIMGRID_PLUGINS_PHOTOVOLTAIC_H_
+
+#include <simgrid/config.h>
+#include <simgrid/forward.h>
+#include <xbt/base.h>
+
+SG_BEGIN_DECL
+
+XBT_PUBLIC void sg_photovoltaic_plugin_init();
+
+XBT_PUBLIC void sg_photovoltaic_set_solar_irradiance(const_sg_host_t host, double s);
+
+XBT_PUBLIC double sg_photovoltaic_get_power(const_sg_host_t host);
+
+SG_END_DECL
+
+#endif
diff --git a/include/simgrid/plugins/task.hpp b/include/simgrid/plugins/task.hpp
new file mode 100644 (file)
index 0000000..e9fa1af
--- /dev/null
@@ -0,0 +1,157 @@
+#ifndef SIMGRID_PLUGINS_TASK_H_
+#define SIMGRID_PLUGINS_TASK_H_
+
+#include <simgrid/s4u/Activity.hpp>
+#include <simgrid/s4u/Io.hpp>
+#include <xbt/Extendable.hpp>
+
+#include <atomic>
+#include <map>
+#include <memory>
+#include <set>
+
+namespace simgrid::plugins {
+
+class Task;
+using TaskPtr = boost::intrusive_ptr<Task>;
+XBT_PUBLIC void intrusive_ptr_release(Task* o);
+XBT_PUBLIC void intrusive_ptr_add_ref(Task* o);
+class ExecTask;
+using ExecTaskPtr = boost::intrusive_ptr<ExecTask>;
+XBT_PUBLIC void intrusive_ptr_release(ExecTask* e);
+XBT_PUBLIC void intrusive_ptr_add_ref(ExecTask* e);
+class CommTask;
+using CommTaskPtr = boost::intrusive_ptr<CommTask>;
+XBT_PUBLIC void intrusive_ptr_release(CommTask* c);
+XBT_PUBLIC void intrusive_ptr_add_ref(CommTask* c);
+class IoTask;
+using IoTaskPtr = boost::intrusive_ptr<IoTask>;
+XBT_PUBLIC void intrusive_ptr_release(IoTask* i);
+XBT_PUBLIC void intrusive_ptr_add_ref(IoTask* i);
+
+struct ExtendedAttributeActivity {
+  static simgrid::xbt::Extension<simgrid::s4u::Activity, ExtendedAttributeActivity> EXTENSION_ID;
+  Task* task_;
+};
+
+class Task {
+  static bool inited_;
+  std::set<Task*> successors_                 = {};
+  std::map<Task*, unsigned int> predecessors_ = {};
+
+  void add_predecessor(Task* predecessor);
+  void remove_predecessor(Task* predecessor);
+  bool ready_to_run() const;
+  void receive(Task* source);
+  void complete();
+
+protected:
+  std::string name_;
+  double amount_;
+  int queued_execs_ = 0;
+  int count_        = 0;
+  bool working_     = false;
+  s4u::ActivityPtr current_activity_;
+  std::vector<std::function<void(Task*)>> end_func_handlers_;
+  std::vector<std::function<void(Task*)>> start_func_handlers_;
+  explicit Task(const std::string& name);
+  virtual ~Task()     = default;
+  virtual void fire() = 0;
+
+  static xbt::signal<void(Task*)> on_start;
+  static xbt::signal<void(Task*)> on_end;
+  std::atomic_int_fast32_t refcount_{0};
+
+public:
+  static void init();
+  const std::string& get_name() const { return name_; }
+  const char* get_cname() const { return name_.c_str(); }
+  void enqueue_execs(int n);
+  void set_amount(double amount);
+  double get_amount() const { return amount_; }
+  void add_successor(TaskPtr t);
+  void remove_successor(TaskPtr t);
+  void remove_all_successors();
+  const std::set<Task*>& get_successors() const { return successors_; }
+  void on_this_start(const std::function<void(Task*)>& func);
+  void on_this_end(const std::function<void(Task*)>& func);
+  int get_count() const;
+
+  /** Add a callback fired before a task activity start.
+   * Triggered after the on_this_start function**/
+  static void on_start_cb(const std::function<void(Task*)>& cb) { on_start.connect(cb); }
+  /** Add a callback fired after a task activity end.
+   * Triggered after the on_this_end function, but before
+   * sending tokens to successors.**/
+  static void on_end_cb(const std::function<void(Task*)>& cb) { on_end.connect(cb); }
+
+#ifndef DOXYGEN
+  friend void intrusive_ptr_release(Task* o)
+  {
+    if (o->refcount_.fetch_sub(1, std::memory_order_release) == 1) {
+      std::atomic_thread_fence(std::memory_order_acquire);
+      delete o;
+    }
+  }
+  friend void intrusive_ptr_add_ref(Task* o) { o->refcount_.fetch_add(1, std::memory_order_relaxed); }
+#endif
+};
+
+class ExecTask : public Task {
+  s4u::Host* host_;
+
+  explicit ExecTask(const std::string& name);
+  void fire() override;
+
+public:
+  static ExecTaskPtr init(const std::string& name);
+  static ExecTaskPtr init(const std::string& name, double flops, s4u::Host* host);
+  ExecTaskPtr set_host(s4u::Host* host);
+  s4u::Host* get_host() const { return host_; }
+  ExecTaskPtr set_flops(double flops);
+  double get_flops() const { return get_amount(); }
+  friend void inline intrusive_ptr_release(ExecTask* e) { intrusive_ptr_release(static_cast<Task*>(e)); }
+  friend void inline intrusive_ptr_add_ref(ExecTask* e) { intrusive_ptr_add_ref(static_cast<Task*>(e)); }
+};
+
+class CommTask : public Task {
+  s4u::Host* source_;
+  s4u::Host* destination_;
+
+  explicit CommTask(const std::string& name);
+  void fire() override;
+
+public:
+  static CommTaskPtr init(const std::string& name);
+  static CommTaskPtr init(const std::string& name, double bytes, s4u::Host* source, s4u::Host* destination);
+  CommTaskPtr set_source(s4u::Host* source);
+  s4u::Host* get_source() const { return source_; }
+  CommTaskPtr set_destination(s4u::Host* destination);
+  s4u::Host* get_destination() const { return destination_; }
+  CommTaskPtr set_bytes(double bytes);
+  double get_bytes() const { return get_amount(); }
+  friend void inline intrusive_ptr_release(CommTask* c) { intrusive_ptr_release(static_cast<Task*>(c)); }
+  friend void inline intrusive_ptr_add_ref(CommTask* c) { intrusive_ptr_add_ref(static_cast<Task*>(c)); }
+};
+
+class IoTask : public Task {
+  s4u::Disk* disk_;
+  s4u::Io::OpType type_;
+  explicit IoTask(const std::string& name);
+  void fire() override;
+
+public:
+  static IoTaskPtr init(const std::string& name);
+  static IoTaskPtr init(const std::string& name, double bytes, s4u::Disk* disk, s4u::Io::OpType type);
+  IoTaskPtr set_disk(s4u::Disk* disk);
+  s4u::Disk* get_disk() const { return disk_; }
+  IoTaskPtr set_bytes(double bytes);
+  double get_bytes() { return get_amount(); }
+  IoTaskPtr set_op_type(s4u::Io::OpType type);
+  s4u::Io::OpType get_op_type() { return type_; }
+
+  friend void inline intrusive_ptr_release(IoTask* i) { intrusive_ptr_release(static_cast<Task*>(i)); }
+  friend void inline intrusive_ptr_add_ref(IoTask* i) { intrusive_ptr_add_ref(static_cast<Task*>(i)); }
+};
+} // namespace simgrid::plugins
+#endif
index 82a880a..2b8175c 100644 (file)
@@ -12,6 +12,7 @@
 #include <simgrid/forward.h>
 #include <stdexcept>
 #include <string>
+#include <string_view>
 #include <vector>
 #include <xbt/Extendable.hpp>
 #include <xbt/asserts.h>
@@ -74,8 +75,8 @@ protected:
   {
     if(this == a)
       throw std::invalid_argument("Cannot be its own successor");
-    auto p = std::find_if(successors_.begin(), successors_.end(), [a](ActivityPtr const& i){ return i.get() == a.get(); });
-    if (p != successors_.end())
+
+    if (std::any_of(begin(successors_), end(successors_), [a](ActivityPtr const& i) { return i.get() == a.get(); }))
       throw std::invalid_argument("Dependency already exists");
 
     successors_.push_back(a);
@@ -87,12 +88,13 @@ protected:
     if(this == a)
       throw std::invalid_argument("Cannot ask to remove itself from successors list");
 
-    auto p = std::find_if(successors_.begin(), successors_.end(), [a](ActivityPtr const& i){ return i.get() == a.get(); });
-    if (p != successors_.end()){
-      successors_.erase(p);
-      a->dependencies_.erase({this});
-    } else
+    auto p =
+        std::find_if(successors_.begin(), successors_.end(), [a](ActivityPtr const& i) { return i.get() == a.get(); });
+    if (p == successors_.end())
       throw std::invalid_argument("Dependency does not exist. Can not be removed.");
+
+    successors_.erase(p);
+    a->dependencies_.erase({this});
   }
 
   static std::set<Activity*>* vetoed_activities_;
@@ -102,23 +104,16 @@ protected:
    * It is forbidden to change the amount of work once the Activity is started */
   Activity* set_remaining(double remains);
 
-private:
-  static xbt::signal<void(Activity&)> on_veto;
-  static xbt::signal<void(Activity const&)> on_completion;
-  static xbt::signal<void(Activity const&)> on_suspended;
-  static xbt::signal<void(Activity const&)> on_resumed;
+  virtual void fire_on_completion() const = 0;
+  virtual void fire_on_this_completion() const = 0;
+  virtual void fire_on_suspend() const = 0;
+  virtual void fire_on_this_suspend() const = 0;
+  virtual void fire_on_resume() const = 0;
+  virtual void fire_on_this_resume() const = 0;
+  virtual void fire_on_veto() const = 0;
+  virtual void fire_on_this_veto() const = 0;
 
 public:
-  /*! Add a callback fired each time that the activity fails to start because of a veto (e.g., unsolved dependency or no
-   * resource assigned) */
-  static void on_veto_cb(const std::function<void(Activity&)>& cb) { on_veto.connect(cb); }
-  /*! Add a callback fired when the activity completes (either normally, cancelled or failed) */
-  static void on_completion_cb(const std::function<void(Activity const&)>& cb) { on_completion.connect(cb); }
-  /*! Add a callback fired when the activity is suspended */
-  static void on_suspended_cb(const std::function<void(Activity const&)>& cb) { on_suspended.connect(cb); }
-  /*! Add a callback fired when the activity is resumed after being suspended */
-  static void on_resumed_cb(const std::function<void(Activity const&)>& cb) { on_resumed.connect(cb); }
-
   XBT_ATTRIB_DEPRECATED_v334("All start() are vetoable now. Please use start() ") void vetoable_start()
   {
     start();
@@ -132,7 +127,8 @@ public:
     } else {
       if (vetoed_activities_ != nullptr)
         vetoed_activities_->insert(this);
-      on_veto(*this);
+      fire_on_veto();
+      fire_on_this_veto();
     }
   }
 
@@ -142,7 +138,8 @@ public:
     // released by the on_completion() callbacks.
     ActivityPtr keepalive(this);
     state_ = state;
-    on_completion(*this);
+    fire_on_completion();
+    fire_on_this_completion();
     if (state == State::FINISHED)
       release_dependencies();
   }
@@ -240,7 +237,42 @@ template <class AnyActivity> class Activity_T : public Activity {
   std::string name_             = "unnamed";
   std::string tracing_category_ = "";
 
+protected:
+  inline static xbt::signal<void(AnyActivity const&)> on_completion;
+  xbt::signal<void(AnyActivity const&)> on_this_completion;
+  inline static xbt::signal<void(AnyActivity const&)> on_suspend;
+  xbt::signal<void(AnyActivity const&)> on_this_suspend;
+  inline static xbt::signal<void(AnyActivity const&)> on_resume;
+  xbt::signal<void(AnyActivity const&)> on_this_resume;
+  inline static xbt::signal<void(AnyActivity&)> on_veto;
+  xbt::signal<void(AnyActivity&)> on_this_veto;
+
 public:
+  /*! \static Add a callback fired when any activity completes (either normally, cancelled or failed) */
+  static void on_completion_cb(const std::function<void(AnyActivity const&)>& cb) { on_completion.connect(cb); }
+  /*! Add a callback fired when this specific activity completes (either normally, cancelled or failed) */
+  void on_this_completion_cb(const std::function<void(AnyActivity const&)>& cb) { on_this_completion.connect(cb); }
+  /*! \static Add a callback fired when any activity is suspended */
+  static void on_suspend_cb(const std::function<void(AnyActivity const&)>& cb) { on_suspend.connect(cb); }
+  /*! Add a callback fired when this specific activity is suspended */
+  void on_this_suspend_cb(const std::function<void(AnyActivity const&)>& cb) { on_this_suspend.connect(cb); }
+  /*! \static Add a callback fired when any activity is resumed after being suspended */
+  static void on_resume_cb(const std::function<void(AnyActivity const&)>& cb) { on_resume.connect(cb); }
+  /*! Add a callback fired when this specific activity is resumed after being suspended */
+  void on_this_resume_cb(const std::function<void(AnyActivity const&)>& cb) { on_this_resume.connect(cb); }
+  /*! \static Add a callback fired each time that any activity fails to start because of a veto (e.g., unsolved
+   *  dependency or no resource assigned) */
+  static void on_veto_cb(const std::function<void(AnyActivity&)>& cb) { on_veto.connect(cb); }
+  /*! Add a callback fired each time that this specific activity fails to start because of a veto (e.g., unsolved
+   *  dependency or no resource assigned) */
+  void on_this_veto_cb(const std::function<void(AnyActivity&)>& cb) { on_this_veto.connect(cb); }
+
+  XBT_ATTRIB_DEPRECATED_v337("Please use on_suspend_cb() instead") static void on_suspended_cb(
+      const std::function<void(Activity const&)>& cb) { on_suspend.connect(cb); }
+  XBT_ATTRIB_DEPRECATED_v337("Please use on_resume_cb() instead") static void on_resumed_cb(
+      const std::function<void(Activity const&)>& cb) { on_resume.connect(cb);  }
+
+
   AnyActivity* add_successor(ActivityPtr a)
   {
     Activity::add_successor(a);
@@ -251,7 +283,7 @@ public:
     Activity::remove_successor(a);
     return static_cast<AnyActivity*>(this);
   }
-  AnyActivity* set_name(const std::string& name)
+  AnyActivity* set_name(std::string_view name)
   {
     name_ = name;
     return static_cast<AnyActivity*>(this);
@@ -259,9 +291,10 @@ public:
   const std::string& get_name() const override { return name_; }
   const char* get_cname() const override { return name_.c_str(); }
 
-  AnyActivity* set_tracing_category(const std::string& category)
+  AnyActivity* set_tracing_category(std::string_view category)
   {
-    xbt_assert(get_state() == State::INITED, "Cannot change the tracing category of an activity after its start");
+    xbt_assert(get_state() == State::INITED || get_state() == State::STARTING,
+               "Cannot change the tracing category of an activity after its start");
     tracing_category_ = category;
     return static_cast<AnyActivity*>(this);
   }
@@ -289,7 +322,9 @@ public:
 
   AnyActivity* cancel() { return static_cast<AnyActivity*>(Activity::cancel()); }
   AnyActivity* wait() { return wait_for(-1.0); }
-  virtual AnyActivity* wait_for(double timeout) { return static_cast<AnyActivity*>(Activity::wait_for(timeout)); }
+  virtual AnyActivity* wait_for(double timeout) {
+    return static_cast<AnyActivity*>(Activity::wait_for(timeout));
+  }
 
 #ifndef DOXYGEN
   /* The refcounting is done in the ancestor class, Activity, but we want each of the classes benefiting of the CRTP
index acdcafe..f3af5b9 100644 (file)
@@ -209,37 +209,60 @@ public:
   int get_refcount() const;
 
   // ***** Actor creation *****
-  /** Retrieve a reference to myself */
+  /** \static
+   * Retrieve a reference to myself
+   */
   static Actor* self();
 
 private:
   static xbt::signal<void(Actor&)> on_creation;
   static xbt::signal<void(Actor const&)> on_suspend;
+  xbt::signal<void(Actor const&)> on_this_suspend;
   static xbt::signal<void(Actor const&)> on_resume;
+  xbt::signal<void(Actor const&)> on_this_resume;
   static xbt::signal<void(Actor const&)> on_sleep;
+  xbt::signal<void(Actor const&)> on_this_sleep;
   static xbt::signal<void(Actor const&)> on_wake_up;
+  xbt::signal<void(Actor const&)> on_this_wake_up;
   static xbt::signal<void(const Actor&, const Host& previous_location)> on_host_change;
+  xbt::signal<void(const Actor&, const Host& previous_location)> on_this_host_change;
   static xbt::signal<void(Actor const&)> on_termination;
+  xbt::signal<void(Actor const&)> on_this_termination;
   static xbt::signal<void(Actor const&)> on_destruction;
+  xbt::signal<void(Actor const&)> on_this_destruction;
 
 public:
-  /** Add a callback fired when a new actor has been created **/
+  /** \static Add a callback fired when a new actor has been created **/
   static void on_creation_cb(const std::function<void(Actor&)>& cb) { on_creation.connect(cb); }
-  /** Add a callback fired when an actor has been suspended**/
+  /** \static Add a callback fired when any actor is suspended (right before the suspend) **/
   static void on_suspend_cb(const std::function<void(Actor const&)>& cb) { on_suspend.connect(cb); }
-  /** Add a callback fired when an actor has been resumed **/
+  /** Add a callback fired when this specific actor is suspended (right before the suspend) **/
+  void on_this_suspend_cb(const std::function<void(Actor const&)>& cb) { on_this_suspend.connect(cb); }
+  /** \static Add a callback fired when any actor is resumed (right before the resume) **/
   static void on_resume_cb(const std::function<void(Actor const&)>& cb) { on_resume.connect(cb); }
-  /** Add a callback fired when an actor starts sleeping **/
+  /** Add a callback fired when this specific actor is resumed (right before the resume) **/
+  void on_this_resume_cb(const std::function<void(Actor const&)>& cb) { on_this_resume.connect(cb); }
+  /** \static Add a callback fired when any actor starts sleeping **/
   static void on_sleep_cb(const std::function<void(Actor const&)>& cb) { on_sleep.connect(cb); }
-  /** Add a callback fired when an actor wakes up from a sleep **/
+  /** Add a callback fired when this specific actor starts sleeping **/
+  void on_this_sleep_cb(const std::function<void(Actor const&)>& cb) { on_this_sleep.connect(cb); }
+  /** \static Add a callback fired when any actor wakes up from a sleep **/
   static void on_wake_up_cb(const std::function<void(Actor const&)>& cb) { on_wake_up.connect(cb); }
-  /** Add a callback fired when an actor is has been migrated to another host **/
+  /** Add a callback fired when this specific actor wakes up from a sleep **/
+  void on_this_wake_up_cb(const std::function<void(Actor const&)>& cb) { on_this_wake_up.connect(cb); }
+  /** \static Add a callback fired when any actor is has been migrated to another host **/
   static void on_host_change_cb(const std::function<void(const Actor&, const Host& previous_location)>& cb)
   {
     on_host_change.connect(cb);
   }
+  /** Add a callback fired when this specific actor is has been migrated to another host **/
+  void on_this_host_change_cb(const std::function<void(const Actor&, const Host& previous_location)>& cb)
+  {
+    on_this_host_change.connect(cb);
+  }
 
-  /** Add a callback fired when an actor terminates its code.
+  /** \static
+   *  Add a callback fired when any actor terminates its code.
    *  @beginrst
    *  The actor may continue to exist if it is still referenced in the simulation, but it's not active anymore.
    *  If you want to free extra data when the actor's destructor is called, use :cpp:func:`Actor::on_destruction_cb`.
@@ -247,19 +270,28 @@ public:
    *  @endrst
    */
   static void on_termination_cb(const std::function<void(Actor const&)>& cb) { on_termination.connect(cb); }
-  /** Add a callback fired when an actor is about to disappear (its destructor was called).
-   *  This signal is fired for any destructed actor, which is mostly useful when designing plugins and extensions.
-   *  If you want to react to the end of the actor's code, use Actor::on_termination instead.
-   *  If you want to register to the termination of a given actor, use this_actor::on_exit() instead.*/
+  /** Add a callback fired when this specific actor terminates its code.
+   *  @beginrst
+   *  The actor may continue to exist if it is still referenced in the simulation, but it's not active anymore.
+   *  If you want to free extra data when the actor's destructor is called, use :cpp:func:`Actor::on_this_destruction_cb`.
+   *  @endrst
+   */
+  void on_this_termination_cb(const std::function<void(Actor const&)>& cb) { on_this_termination.connect(cb); }
+  /** \static  Add a callback fired when an actor is about to disappear (its destructor was called).
+   *  This signal is fired for any destructed actor, which is mostly useful when designing plugins and extensions. */
   static void on_destruction_cb(const std::function<void(Actor const&)>& cb) { on_destruction.connect(cb); }
+  /** Add a callback fired when this specific actor is about to disappear (its destructor was called). */
+  void on_this_destruction_cb(const std::function<void(Actor const&)>& cb) { on_this_destruction.connect(cb); }
 
-  /** Create an actor from a @c std::function<void()>.
+  /** \static
+   *  Create an actor from a @c std::function<void()>.
    *  If the actor is restarted, it gets a fresh copy of the function.
    *  @verbatim embed:rst:inline See the :ref:`example <s4u_ex_actors_create>`. @endverbatim */
   static ActorPtr create(const std::string& name, s4u::Host* host, const std::function<void()>& code);
-  /** Create an actor, but don't start it yet.
+  /** \static
+   *  Create an actor, but don't start it yet.
    *
-   * This is useful to set some properties or extension before actually starting it */
+   *  This is useful to set some properties or extension before actually starting it */
   static ActorPtr init(const std::string& name, s4u::Host* host);
   ActorPtr set_stacksize(unsigned stacksize);
   /** Start a previously initialized actor */
@@ -280,14 +312,16 @@ public:
 
   ActorPtr start(const std::function<void()>& code, std::vector<std::string> args);
 
-  /** Create an actor from a callable thing.
+  /** \static
+   * Create an actor from a callable thing.
    *  @verbatim embed:rst:inline See the :ref:`example <s4u_ex_actors_create>`. @endverbatim */
   template <class F> static ActorPtr create(const std::string& name, s4u::Host* host, F code)
   {
     return create(name, host, std::function<void()>(std::move(code)));
   }
 
-  /** Create an actor using a callable thing and its arguments.
+  /** \static
+   * Create an actor using a callable thing and its arguments.
    *
    * Note that the arguments will be copied, so move-only parameters are forbidden.
    * @verbatim embed:rst:inline See the :ref:`example <s4u_ex_actors_create>`. @endverbatim */
@@ -303,7 +337,8 @@ public:
     return create(name, host, std::bind(std::move(code), std::move(args)...));
   }
 
-  /** Create actor from function name and a vector of strings as arguments.
+  /** \static
+   *  Create actor from function name and a vector of strings as arguments.
    *  @verbatim embed:rst:inline See the :ref:`example <s4u_ex_actors_create>`. @endverbatim */
   static ActorPtr create(const std::string& name, s4u::Host* host, const std::string& function,
                          std::vector<std::string> args);
@@ -318,6 +353,7 @@ public:
 
   /** Returns whether or not this actor has been daemonized or not **/
   bool is_daemon() const;
+
   static bool is_maestro();
 
   /** Retrieves the name of that actor as a C++ string */
@@ -391,7 +427,9 @@ public:
    */
   void kill();
 
-  /** Retrieves the actor that have the given PID (or nullptr if not existing) */
+  /** \static
+    * Retrieves the actor that have the given PID (or nullptr if not existing)
+  */
   static ActorPtr by_pid(aid_t pid);
 
   /** Wait for the actor to finish.
@@ -410,7 +448,9 @@ public:
   /** Kill that actor and restart it from start. */
   Actor* restart();
 
-  /** Kill all actors (but the issuer). Being killed is not something that actors can delay or avoid. */
+  /** \static
+   * Kill all actors (but the issuer). Being killed is not something that actors can delay or avoid.
+  */
   static void kill_all();
 
   /** Returns the internal implementation of this actor */
index c031859..2d6db86 100644 (file)
@@ -15,8 +15,7 @@
 #include <atomic>
 #include <future>
 
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
 
 class XBT_PUBLIC Barrier {
   kernel::activity::BarrierImpl* pimpl_;
@@ -30,7 +29,7 @@ public:
   Barrier& operator=(Barrier const&) = delete;
 #endif
 
-  /** Creates a barrier for the given amount of actors */
+  /** \static Creates a barrier for the given amount of actors */
   static BarrierPtr create(unsigned int expected_actors);
   /** Blocks into the barrier. Every waiting actors will be unlocked once the expected amount of actors reaches the barrier */
   int wait();
@@ -43,7 +42,6 @@ public:
   friend XBT_PUBLIC void intrusive_ptr_release(Barrier* barrier);
 #endif
 };
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
 
 #endif
index 8d1d856..2f07115 100644 (file)
 #include <string>
 #include <vector>
 
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
 /** @brief Communication async
  *
  * Represents all asynchronous communications, that you can test or wait onto.
  */
 class XBT_PUBLIC Comm : public Activity_T<Comm> {
   friend Mailbox; // Factory of comms
+  friend kernel::activity::CommImpl;
   /* specified for normal mailbox-based communications*/
   Mailbox* mailbox_                   = nullptr;
   kernel::actor::ActorImpl* sender_   = nullptr;
@@ -39,19 +39,45 @@ class XBT_PUBLIC Comm : public Activity_T<Comm> {
   Comm() = default;
   Comm* do_start() override;
 
-public:
-  /* signals and related callbacks */
-#ifndef DOXYGEN
-  /* FIXME signals should be private */
+protected:
   static xbt::signal<void(Comm const&)> on_send;
+  xbt::signal<void(Comm const&)> on_this_send;
   static xbt::signal<void(Comm const&)> on_recv;
-  static xbt::signal<void(Comm const&)> on_start;
-#endif
+  xbt::signal<void(Comm const&)> on_this_recv;
+  inline static xbt::signal<void(Comm const&)> on_start;
+  xbt::signal<void(Comm const&)> on_this_start;
+
+  void fire_on_completion() const override {
+    /* The completion signal of a Comm has to be thrown only once and not by the sender AND the receiver.
+       then Comm::on_completion is thrown in the kernel in CommImpl::finish.
+     */
+  }
+  void fire_on_this_completion() const override {
+    /* The completion signal of a Comm has to be thrown only once and not by the sender AND the receiver.
+       then Comm::on_completion is thrown in the kernel in CommImpl::finish.
+     */
+  }
+  void fire_on_suspend() const override { on_suspend(*this); }
+  void fire_on_this_suspend() const override { on_this_suspend(*this); }
+  void fire_on_resume() const override { on_resume(*this); }
+  void fire_on_this_resume() const override { on_this_resume(*this); }
+  void fire_on_veto() const override { on_veto(const_cast<Comm&>(*this)); }
+  void fire_on_this_veto() const override { on_this_veto(const_cast<Comm&>(*this)); }
 
+public:
+  /*! \static Add a callback fired when the send of any Comm is posted  */
   static void on_send_cb(const std::function<void(Comm const&)>& cb) { on_send.connect(cb); }
+  /*! Add a callback fired when the send of this specific Comm is posted  */
+  void on_this_send_cb(const std::function<void(Comm const&)>& cb) { on_send.connect(cb); }
+  /*! \static Add a callback fired when the recv of any Comm is posted  */
   static void on_recv_cb(const std::function<void(Comm const&)>& cb) { on_recv.connect(cb); }
+  /*! Add a callback fired when the recv of this specific Comm is posted  */
+  void on_this_recv_cb(const std::function<void(Comm const&)>& cb) { on_this_recv.connect(cb); }
+  /*! \static Add a callback fired when any Comm starts  */
   static void on_start_cb(const std::function<void(Comm const&)>& cb) { on_start.connect(cb); }
-  /* More callbacks */
+  /*!  Add a callback fired when this specific Comm starts  */
+  void on_this_start_cb(const std::function<void(Comm const&)>& cb) { on_this_start.connect(cb); }
+
   CommPtr set_copy_data_callback(const std::function<void(kernel::activity::CommImpl*, void*, size_t)>& callback);
   XBT_ATTRIB_DEPRECATED_v337("Please manifest if you actually need this function") static void copy_buffer_callback(
       kernel::activity::CommImpl*, void*, size_t);
@@ -70,7 +96,8 @@ public:
                    const std::function<void(simgrid::kernel::activity::CommImpl*, void*, size_t)>& copy_data_fun,
                    void* data, double timeout, double rate);
 
-  /* "One-sided" communications. This way of communicating bypasses the mailbox and actors mechanism. It creates a
+  /* \static
+   * "One-sided" communications. This way of communicating bypasses the mailbox and actors mechanism. It creates a
    * communication (vetoabled, asynchronous, or synchronous) directly between two hosts. There is really no limit on
    * the hosts involved. In particular, the actor creating such a communication does not have to be on one of the
    * involved hosts! Enjoy the comfort of the simulator :)
@@ -149,6 +176,7 @@ public:
 
   bool is_assigned() const override;
   Actor* get_sender() const;
+  Actor* get_receiver() const;
 
   /* Comm life cycle */
   /** Start the comm, and ignore its result. It can be completely forgotten after that. */
@@ -162,22 +190,21 @@ public:
 
   Comm* wait_for(double timeout) override;
 
-  /*! take a vector s4u::CommPtr and return the rank of the first finished one (or -1 if none is done). */
+  /*! \static take a vector s4u::CommPtr and return the rank of the first finished one (or -1 if none is done). */
   static ssize_t test_any(const std::vector<CommPtr>& comms);
 
-  /*! take a vector s4u::CommPtr and return when one of them is finished.
+  /*! \static take a vector s4u::CommPtr and return when one of them is finished.
    * The return value is the rank of the first finished CommPtr. */
   static ssize_t wait_any(const std::vector<CommPtr>& comms) { return wait_any_for(comms, -1); }
-  /*! Same as wait_any, but with a timeout. Return -1 if the timeout occurs.*/
+  /*! \static Same as wait_any, but with a timeout. Return -1 if the timeout occurs.*/
   static ssize_t wait_any_for(const std::vector<CommPtr>& comms, double timeout);
 
-  /*! take a vector s4u::CommPtr and return when all of them is finished. */
+  /*! \static take a vector s4u::CommPtr and return when all of them is finished. */
   static void wait_all(const std::vector<CommPtr>& comms);
-  /*! Same as wait_all, but with a timeout. Return the number of terminated comm (less than comms.size() if the timeout
-   * occurs). */
+  /*! \static Same as wait_all, but with a timeout. Return the number of terminated comm (less than comms.size() if
+   *  the timeout occurs). */
   static size_t wait_all_for(const std::vector<CommPtr>& comms, double timeout);
 };
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
 
 #endif /* SIMGRID_S4U_COMM_HPP */
index 56c74c3..f2f0744 100644 (file)
@@ -14,8 +14,7 @@
 
 #include <future>
 
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
 
 /**
  * @beginrst
@@ -44,7 +43,7 @@ private:
 #endif
 
 public:
-  /** Create a new condition variable and return a smart pointer
+  /** \static Create a new condition variable and return a smart pointer
    *
    * @beginrst
    * You should only manipulate :cpp:type:`simgrid::s4u::ConditionVariablePtr`, as created by this function (see also :ref:`s4u_raii`).
@@ -117,7 +116,6 @@ public:
   void notify_all();
 };
 
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
 
 #endif
index fcba914..8a09719 100644 (file)
@@ -133,17 +133,35 @@ public:
   Disk* seal();
 
   /* The signals */
-  /** @brief Add a callback fired when a new Disk is created */
+  /** @brief \static Add a callback fired when a new Disk is created */
   static void on_creation_cb(const std::function<void(Disk&)>& cb) { on_creation.connect(cb); }
-  /** @brief Add a callback fired when a Disk is destroyed */
+  /** @brief \static Add a callback fired when any Disk is destroyed */
   static void on_destruction_cb(const std::function<void(Disk const&)>& cb) { on_destruction.connect(cb); }
-  /** @brief Add a callback fired when a Disk's state changes */
-  static void on_state_change_cb(const std::function<void(Disk const&)>& cb) { on_state_change.connect(cb); }
+  /** @brief Add a callback fired when this specific Disk is destroyed */
+  void on_this_destruction_cb(const std::function<void(Disk const&)>& cb) { on_this_destruction.connect(cb); }
+  /** @brief \static Add a callback fired when any Disk is turned on or off */
+  static void on_onoff_cb(const std::function<void(Disk const&)>& cb)
+  {
+    on_onoff.connect(cb);
+  }
+  /** @brief Add a callback fired when this specific Disk is turned on or off */
+  void on_this_onoff_cb(const std::function<void(Disk const&)>& cb)
+  {
+    on_this_onoff.connect(cb);
+  }
+
+  XBT_ATTRIB_DEPRECATED_v337("Please use on_onoff_cb() instead") static void on_state_change_cb(
+      const std::function<void(Disk const&)>& cb)
+  {
+    on_onoff.connect(cb);
+  }
 
 private:
   static xbt::signal<void(Disk&)> on_creation;
   static xbt::signal<void(Disk const&)> on_destruction;
-  static xbt::signal<void(Disk const&)> on_state_change;
+  xbt::signal<void(Disk const&)> on_this_destruction;
+  static xbt::signal<void(Disk const&)> on_onoff;
+  xbt::signal<void(Disk const&)> on_this_onoff;
 };
 
 } // namespace s4u
index 742bb36..beff27c 100644 (file)
@@ -18,8 +18,7 @@
 #include <utility>
 #include <vector>
 
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
 /** @brief Simulation engine
  *
  * This is a singleton containing all the main functions of the simulation.
@@ -193,7 +192,7 @@ public:
   /** @brief Retrieves all netzones of the type indicated by the template argument */
   template <class T> std::vector<T*> get_filtered_netzones() const
   {
-    static_assert(std::is_base_of<kernel::routing::NetZoneImpl, T>::value,
+    static_assert(std::is_base_of_v<kernel::routing::NetZoneImpl, T>,
                   "Filtering netzones is only possible for subclasses of kernel::routing::NetZoneImpl");
     std::vector<T*> res;
     get_filtered_netzones_recursive(get_netzone_root(), &res);
@@ -272,7 +271,7 @@ std::vector<ActivityPtr> create_DAG_from_json(const std::string& filename);
 template <class T>
 XBT_PRIVATE void get_filtered_netzones_recursive(const s4u::NetZone* current, std::vector<T*>* whereto)
 {
-  static_assert(std::is_base_of<kernel::routing::NetZoneImpl, T>::value,
+  static_assert(std::is_base_of_v<kernel::routing::NetZoneImpl, T>,
                 "Filtering netzones is only possible for subclasses of kernel::routing::NetZoneImpl");
   for (auto const& elem : current->get_children()) {
     get_filtered_netzones_recursive(elem, whereto);
@@ -282,7 +281,6 @@ XBT_PRIVATE void get_filtered_netzones_recursive(const s4u::NetZone* current, st
   }
 }
 #endif
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
 
 #endif /* SIMGRID_S4U_ENGINE_HPP */
index cd514ea..5cd7857 100644 (file)
@@ -11,8 +11,7 @@
 #include <simgrid/s4u/Actor.hpp>
 #include <xbt/ex.h>
 
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
 
 /** Computation Activity, representing the asynchronous executions.
  *
@@ -43,22 +42,34 @@ protected:
 
   void reset() const;
 
-  static xbt::signal<void(Exec const&)> on_start;
+  inline static xbt::signal<void(Exec const&)> on_start;
+  xbt::signal<void(Exec const&)> on_this_start;
+  void fire_on_completion() const override { on_completion(*this); }
+  void fire_on_this_completion() const override { on_this_completion(*this); }
+  void fire_on_suspend() const override { on_suspend(*this); }
+  void fire_on_this_suspend() const override { on_this_suspend(*this); }
+  void fire_on_resume() const override { on_resume(*this); }
+  void fire_on_this_resume() const override { on_this_resume(*this); }
+  void fire_on_veto() const override { on_veto(const_cast<Exec&>(*this)); }
+  void fire_on_this_veto() const override { on_this_veto(const_cast<Exec&>(*this)); }
 
 public:
 #ifndef DOXYGEN
   Exec(Exec const&) = delete;
   Exec& operator=(Exec const&) = delete;
 #endif
-  /*! Signal fired each time that an execution actually starts (no veto) */
+  /*! \static Signal fired each time that any execution actually starts (no veto) */
   static void on_start_cb(const std::function<void(Exec const&)>& cb) { on_start.connect(cb); }
+  /*! Signal fired each time that this specific execution actually starts (no veto) */
+  void on_this_start_cb(const std::function<void(Exec const&)>& cb) { on_this_start.connect(cb); }
 
+  /*! \static Initiate the creation of an Exec. Setters have to be called afterwards */
   static ExecPtr init();
 
-  /*! take a vector of s4u::ExecPtr and return when one of them is finished.
+  /*! \static take a vector of s4u::ExecPtr and return when one of them is finished.
    * The return value is the rank of the first finished ExecPtr. */
   static ssize_t wait_any(const std::vector<ExecPtr>& execs) { return wait_any_for(execs, -1); }
-  /*! Same as wait_any, but with a timeout. If the timeout occurs, parameter last is returned.*/
+  /*! \static Same as wait_any, but with a timeout. If the timeout occurs, parameter last is returned.*/
   static ssize_t wait_any_for(const std::vector<ExecPtr>& execs, double timeout);
 
   /** @brief On sequential executions, returns the amount of flops that remain to be done; This cannot be used on
@@ -88,7 +99,6 @@ public:
   bool is_assigned() const override;
 };
 
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
 
 #endif /* SIMGRID_S4U_EXEC_HPP */
index a06ef84..d29468e 100644 (file)
@@ -7,6 +7,7 @@
 #define SIMGRID_S4U_HOST_HPP
 
 #include <simgrid/forward.h>
+#include <simgrid/kernel/resource/Action.hpp>
 #include <xbt/Extendable.hpp>
 #include <xbt/signal.hpp>
 
@@ -46,6 +47,9 @@ class XBT_PUBLIC Host : public xbt::Extendable<Host> {
 
   kernel::resource::CpuImpl* pimpl_cpu_      = nullptr;
   kernel::routing::NetPoint* pimpl_netpoint_ = nullptr;
+#ifndef DOXYGEN
+  friend kernel::resource::CpuAction; // signal exec_state_changed
+#endif
 
 public:
   explicit Host(kernel::resource::HostImpl* pimpl) : pimpl_(pimpl) {}
@@ -56,20 +60,55 @@ protected:
 
   static xbt::signal<void(Host&)> on_creation;
   static xbt::signal<void(Host const&)> on_destruction;
+  xbt::signal<void(Host const&)> on_this_destruction;
+  static xbt::signal<void(kernel::resource::CpuAction&, kernel::resource::Action::State)> on_exec_state_change;
 
 public:
   static xbt::signal<void(Host const&)> on_speed_change;
-  static xbt::signal<void(Host const&)> on_state_change;
+  xbt::signal<void(Host const&)> on_this_speed_change;
+  static xbt::signal<void(Host const&)> on_onoff;
+  xbt::signal<void(Host const&)> on_this_onoff;
+
 #endif
-  /** Add a callback fired on each newly created host */
+  /** \static Add a callback fired on each newly created host */
   static void on_creation_cb(const std::function<void(Host&)>& cb) { on_creation.connect(cb); }
-  /** Add a callback fired when the machine is turned on or off (called AFTER the change) */
-  static void on_state_change_cb(const std::function<void(Host const&)>& cb) { on_state_change.connect(cb); }
-  /** Add a callback fired when the speed of the machine is changed (called AFTER the change)
+  /** \static Add a callback fired when any machine is turned on or off (called AFTER the change) */
+  static void on_onoff_cb(const std::function<void(Host const&)>& cb)
+  {
+    on_onoff.connect(cb);
+  }
+  XBT_ATTRIB_DEPRECATED_v337("Please use on_onoff_cb() instead") static void on_state_change_cb(
+      const std::function<void(Host const&)>& cb)
+  {
+    on_onoff.connect(cb);
+  }
+  /** Add a callback fired when this specific machine is turned on or off (called AFTER the change) */
+  void on_this_onoff_cb(const std::function<void(Host const&)>& cb)
+  {
+    on_this_onoff.connect(cb);
+  }
+  /** \static Add a callback fired when the speed of any machine is changed (called AFTER the change)
    * (either because of a pstate switch or because of an external load event coming from the profile) */
   static void on_speed_change_cb(const std::function<void(Host const&)>& cb) { on_speed_change.connect(cb); }
-  /** Add a callback fired just before destructing a host */
+  /** Add a callback fired when the speed of this specific machine is changed (called AFTER the change)
+   * (either because of a pstate switch or because of an external load event coming from the profile) */
+  void on_this_speed_change_cb(const std::function<void(Host const&)>& cb)
+  {
+    on_this_speed_change.connect(cb);
+  }
+  /** \static Add a callback fired just before destructing any host */
   static void on_destruction_cb(const std::function<void(Host const&)>& cb) { on_destruction.connect(cb); }
+  /** Add a callback fired just before destructing this specific host */
+  void on_this_destruction_cb(const std::function<void(Host const&)>& cb)
+  {
+    on_this_destruction.connect(cb);
+  }
+  /** \static Add a callback fired when the state of any exec activity changes */
+  static void on_exec_state_change_cb(
+      const std::function<void(kernel::resource::CpuAction&, kernel::resource::Action::State previous)>& cb)
+  {
+    on_exec_state_change.connect(cb);
+  }
 
   virtual void destroy();
 #ifndef DOXYGEN
@@ -78,11 +117,11 @@ public:
   Host& operator=(Host const&) = delete;
 #endif
 
-  /** Retrieve a host from its name, or return nullptr */
+  /** \static Retrieve a host from its name, or return nullptr */
   static Host* by_name_or_null(const std::string& name);
-  /** Retrieve a host from its name, or die */
+  /** \static Retrieve a host from its name, or die */
   static Host* by_name(const std::string& name);
-  /** Retrieves the host on which the running actor is located */
+  /** \static Retrieves the host on which the running actor is located */
   static Host* current();
 
   /** Retrieves the name of that host as a C++ string */
@@ -141,7 +180,7 @@ public:
   Host* set_concurrency_limit(int limit);
   int get_concurrency_limit() const;
 
-  /** @brief Convert the CPU's speed from string to double */
+  /** \static @brief Convert the CPU's speed from string to double */
   static std::vector<double> convert_pstate_speed_vector(const std::vector<std::string>& speed_per_state);
   /**
    * @brief Set the CPU's speed
index 8c626f7..3cc28a0 100644 (file)
@@ -11,8 +11,7 @@
 
 #include <string>
 
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
 
 /** I/O Activity, representing the asynchronous disk access.
  *
@@ -25,22 +24,35 @@ class XBT_PUBLIC Io : public Activity_T<Io> {
   friend kernel::EngineImpl;
 #endif
 
-  static xbt::signal<void(Io const&)> on_start;
+  inline static xbt::signal<void(Io const&)> on_start;
+  xbt::signal<void(Io const&)> on_this_start;
 
 protected:
   explicit Io(kernel::activity::IoImplPtr pimpl);
   Io* do_start() override;
+  void fire_on_completion() const override { on_completion(*this); }
+  void fire_on_this_completion() const override { on_this_completion(*this); }
+  void fire_on_suspend() const override { on_suspend(*this); }
+  void fire_on_this_suspend() const override { on_this_suspend(*this); }
+  void fire_on_resume() const override { on_resume(*this); }
+  void fire_on_this_resume() const override { on_this_resume(*this); }
+  void fire_on_veto() const override { on_veto(const_cast<Io&>(*this)); }
+  void fire_on_this_veto() const override { on_this_veto(const_cast<Io&>(*this)); }
 
 public:
   enum class OpType { READ, WRITE };
 
+   /*! \static Signal fired each time that any I/O actually starts (no veto) */
   static void on_start_cb(const std::function<void(Io const&)>& cb) { on_start.connect(cb); }
+   /*! Signal fired each time this specific I/O actually starts (no veto) */
+  void on_this_start_cb(const std::function<void(Io const&)>& cb) { on_this_start.connect(cb); }
 
+   /*! \static Initiate the creation of an I/O. Setters have to be called afterwards */
   static IoPtr init();
-  /*! take a vector of s4u::IoPtr and return when one of them is finished.
+  /*! \static take a vector of s4u::IoPtr and return when one of them is finished.
    * The return value is the rank of the first finished IoPtr. */
   static ssize_t wait_any(const std::vector<IoPtr>& ios) { return wait_any_for(ios, -1); }
-  /*! Same as wait_any, but with a timeout. If the timeout occurs, parameter last is returned.*/
+  /*! \static Same as wait_any, but with a timeout. If the timeout occurs, parameter last is returned.*/
   static ssize_t wait_any_for(const std::vector<IoPtr>& ios, double timeout);
 
   double get_remaining() const override;
@@ -64,7 +76,6 @@ public:
   bool is_assigned() const override;
 };
 
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
 
 #endif /* SIMGRID_S4U_IO_HPP */
index ca87ddc..3e0832b 100644 (file)
@@ -49,7 +49,7 @@ public:
 
   kernel::resource::StandardLinkImpl* get_impl() const;
 
-  /** @brief Retrieve a link from its name */
+  /** \static @brief Retrieve a link from its name */
   static Link* by_name(const std::string& name);
   static Link* by_name_or_null(const std::string& name);
 
@@ -157,29 +157,56 @@ public:
 private:
 #ifndef DOXYGEN
   static xbt::signal<void(Link&)> on_creation;
-  static xbt::signal<void(Link const&)> on_state_change;
+  static xbt::signal<void(Link const&)> on_onoff;
+  xbt::signal<void(Link const&)> on_this_onoff;
   static xbt::signal<void(Link const&)> on_bandwidth_change;
+  xbt::signal<void(Link const&)> on_this_bandwidth_change;
   static xbt::signal<void(kernel::resource::NetworkAction&, kernel::resource::Action::State)>
       on_communication_state_change;
   static xbt::signal<void(Link const&)> on_destruction;
+  xbt::signal<void(Link const&)> on_this_destruction;
 #endif
 
 public:
   /* The signals */
-  /** @brief Add a callback fired when a new Link is created */
+  /** \static @brief Add a callback fired when a new Link is created */
   static void on_creation_cb(const std::function<void(Link&)>& cb) { on_creation.connect(cb); }
-  /** @brief Add a callback fired when the state of a Link changes (when it is turned on or off) */
-  static void on_state_change_cb(const std::function<void(Link const&)>& cb) { on_state_change.connect(cb); }
-  /** @brief Add a callback fired when the bandwidth of a Link changes */
+  /** \static @brief Add a callback fired when any Link is turned on or off */
+  static void on_onoff_cb(const std::function<void(Link const&)>& cb)
+  {
+    on_onoff.connect(cb);
+  }
+  /** @brief Add a callback fired when this specific Link is turned on or off */
+  void on_this_onoff_cb(const std::function<void(Link const&)>& cb)
+  {
+    on_this_onoff.connect(cb);
+  }
+  /** \static @brief Add a callback fired when the bandwidth of any Link changes */
   static void on_bandwidth_change_cb(const std::function<void(Link const&)>& cb) { on_bandwidth_change.connect(cb); }
-  /** @brief Add a callback fired when a communication changes it state (ready/done/cancel) */
+  /** @brief Add a callback fired when the bandwidth of this specific Link changes */
+  void on_this_bandwidth_change_cb(const std::function<void(Link const&)>& cb)
+  {
+    on_this_bandwidth_change.connect(cb);
+  }
+  /** \static @brief Add a callback fired when a communication changes it state (ready/done/cancel) */
   static void on_communication_state_change_cb(
       const std::function<void(kernel::resource::NetworkAction&, kernel::resource::Action::State)>& cb)
   {
     on_communication_state_change.connect(cb);
   }
-  /** @brief Add a callback fired when a Link is destroyed */
+  /** \static @brief Add a callback fired when any Link is destroyed */
   static void on_destruction_cb(const std::function<void(Link const&)>& cb) { on_destruction.connect(cb); }
+  /** @brief Add a callback fired when this specific Link is destroyed */
+  void on_this_destruction_cb(const std::function<void(Link const&)>& cb)
+  {
+    on_this_destruction.connect(cb);
+  }
+
+  XBT_ATTRIB_DEPRECATED_v337("Please use on_onoff_cb() instead") static void on_state_change_cb(
+      const std::function<void(Link const&)>& cb)
+  {
+    on_onoff.connect(cb);
+  }
 };
 
 /**
@@ -197,7 +224,7 @@ public:
   /** @brief Get the link direction down */
   Link* get_link_down() const;
 
-  /** @brief Retrieve a link from its name */
+  /** \static @brief Retrieve a link from its name */
   static SplitDuplexLink* by_name(const std::string& name);
 };
 
index 75201ec..b5723df 100644 (file)
@@ -14,8 +14,7 @@
 #include <memory>
 #include <string>
 
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
 
 /** @brief Mailboxes: Network rendez-vous points. */
 class XBT_PUBLIC Mailbox {
@@ -39,7 +38,7 @@ public:
   /** @brief Retrieves the name of that mailbox as a C string */
   const char* get_cname() const;
 
-  /** Retrieve the mailbox associated to the given name. Mailboxes are created on demand. */
+  /** \static Retrieve the mailbox associated to the given name. Mailboxes are created on demand. */
   static Mailbox* by_name(const std::string& name);
 
   /** Returns whether the mailbox contains queued communications */
@@ -151,7 +150,6 @@ template <typename T> T* Mailbox::get(double timeout)
   get_async<T>(&res)->wait_for(timeout);
   return res;
 }
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
 
 #endif /* SIMGRID_S4U_MAILBOX_HPP */
index 60eb8d0..7791cd7 100644 (file)
@@ -9,8 +9,7 @@
 #include <simgrid/forward.h>
 #include <xbt/asserts.h>
 
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
 
 /** @brief A classical mutex, but blocking in the simulation world.
  *
@@ -48,14 +47,13 @@ class XBT_PUBLIC Mutex {
 #endif
 
 public:
-  /** Constructs a new mutex */
+  /** \static Constructs a new mutex */
   static MutexPtr create();
   void lock();
   void unlock();
   bool try_lock();
 };
 
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
 
 #endif /* SIMGRID_S4U_MUTEX_HPP */
index 63a1c23..97ecfbb 100644 (file)
@@ -18,8 +18,7 @@
 #include <utility>
 #include <vector>
 
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
 
 /** @brief Networking Zones
  *
@@ -94,7 +93,9 @@ private:
 #endif
 
 public:
+  /** \static Add a callback fired on each newly created NetZone */
   static void on_creation_cb(const std::function<void(NetZone const&)>& cb) { on_creation.connect(cb); }
+  /** \static Add a callback fired on each newly sealed NetZone */
   static void on_seal_cb(const std::function<void(NetZone const&)>& cb) { on_seal.connect(cb); }
 
   /**
@@ -329,7 +330,6 @@ XBT_PUBLIC NetZone* create_dragonfly_zone(const std::string& name, const NetZone
                                           const DragonflyParams& parameters, const ClusterCallbacks& set_callbacks,
                                           double bandwidth, double latency, Link::SharingPolicy sharing_policy);
 
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
 
 #endif /* SIMGRID_S4U_NETZONE_HPP */
index 6dcb9a4..4c16f7a 100644 (file)
@@ -8,8 +8,7 @@
 
 #include <simgrid/forward.h>
 
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
 
 /** @brief A classical semaphore, but blocking in the simulation world
  *
@@ -46,7 +45,7 @@ class XBT_PUBLIC Semaphore {
 #endif
 
 public:
-  /** Constructs a new semaphore */
+  /** \static Constructs a new semaphore */
   static SemaphorePtr create(unsigned int initial_capacity);
 
   void acquire();
@@ -57,7 +56,6 @@ public:
   bool would_block() const;
 };
 
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
 
 #endif /* SIMGRID_S4U_SEMAPHORE_HPP */
index 748f264..6733ba3 100644 (file)
@@ -10,8 +10,7 @@
 #include <simgrid/s4u/Host.hpp>
 #include <xbt/utility.hpp>
 
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
 
 /** @brief Host extension for the VMs */
 class VmHostExt {
@@ -37,13 +36,21 @@ class XBT_PUBLIC VirtualMachine : public s4u::Host {
   /* Signals about the life cycle of the VM */
   static xbt::signal<void(VirtualMachine&)> on_vm_creation;
   static xbt::signal<void(VirtualMachine const&)> on_start;
+  xbt::signal<void(VirtualMachine const&)> on_this_start;
   static xbt::signal<void(VirtualMachine const&)> on_started;
+  xbt::signal<void(VirtualMachine const&)> on_this_started;
   static xbt::signal<void(VirtualMachine const&)> on_shutdown;
+  xbt::signal<void(VirtualMachine const&)> on_this_shutdown;
   static xbt::signal<void(VirtualMachine const&)> on_suspend;
+  xbt::signal<void(VirtualMachine const&)> on_this_suspend;
   static xbt::signal<void(VirtualMachine const&)> on_resume;
+  xbt::signal<void(VirtualMachine const&)> on_this_resume;
   static xbt::signal<void(VirtualMachine const&)> on_migration_start;
+  xbt::signal<void(VirtualMachine const&)> on_this_migration_start;
   static xbt::signal<void(VirtualMachine const&)> on_migration_end;
+  xbt::signal<void(VirtualMachine const&)> on_this_migration_end;
   static xbt::signal<void(VirtualMachine const&)> on_vm_destruction;
+  xbt::signal<void(VirtualMachine const&)> on_this_vm_destruction;
 
 #ifndef DOXYGEN
   friend kernel::resource::VirtualMachineImpl; // calls signals from Impl
@@ -89,23 +96,71 @@ public:
   State get_state() const;
 
   /* Callbacks on signals */
+  /*! \static Add a callback fired when any VM is created */
   static void on_creation_cb(const std::function<void(VirtualMachine&)>& cb) { on_vm_creation.connect(cb); }
+  /*! \static Add a callback fired when any VM starts */
   static void on_start_cb(const std::function<void(VirtualMachine const&)>& cb) { on_start.connect(cb); }
+  /*! Add a callback fired when this specific VM starts */
+  void on_this_start_cb(const std::function<void(VirtualMachine const&)>& cb)
+  {
+    on_this_start.connect(cb);
+  }
+  /*! \static Add a callback fired when any VM is actually started */
   static void on_started_cb(const std::function<void(VirtualMachine const&)>& cb) { on_started.connect(cb); }
+  /*! Add a callback fired when this specific VM is actually started */
+  void on_this_started_cb(const std::function<void(VirtualMachine const&)>& cb)
+  {
+    on_this_started.connect(cb);
+  }
+  /*! \static Add a callback fired when any VM is shut down */
   static void on_shutdown_cb(const std::function<void(VirtualMachine const&)>& cb) { on_shutdown.connect(cb); }
+  /*! Add a callback fired when this specific VM is shut down */
+  void on_this_shutdown_cb(const std::function<void(VirtualMachine const&)>& cb)
+  {
+    on_this_shutdown.connect(cb);
+  }
+  /*! \static Add a callback fired when any VM is suspended*/
   static void on_suspend_cb(const std::function<void(VirtualMachine const&)>& cb) { on_suspend.connect(cb); }
+  /*! Add a callback fired when this specific VM is suspended*/
+  void on_this_suspend_cb(const std::function<void(VirtualMachine const&)>& cb)
+  {
+    on_this_suspend.connect(cb);
+  }
+  /*! \static Add a callback fired when any VM is resumed*/
   static void on_resume_cb(const std::function<void(VirtualMachine const&)>& cb) { on_resume.connect(cb); }
+  /*! Add a callback fired when this specific VM is resumed*/
+  void on_this_resume_cb(const std::function<void(VirtualMachine const&)>& cb)
+  {
+    on_this_resume.connect(cb);
+  }
+  /*! \static Add a callback fired when any VM is destroyed*/
   static void on_destruction_cb(const std::function<void(VirtualMachine const&)>& cb) { on_vm_destruction.connect(cb); }
+  /*! Add a callback fired when this specific VM is destroyed*/
+  void on_this_destruction_cb(const std::function<void(VirtualMachine const&)>& cb)
+  {
+    on_this_vm_destruction.connect(cb);
+  }
+  /*! \static Add a callback fired when any VM starts a migration*/
   static void on_migration_start_cb(const std::function<void(VirtualMachine const&)>& cb)
   {
     on_migration_start.connect(cb);
   }
+  /*! Add a callback fired when this specific VM starts a migration*/
+  void on_this_migration_start_cb(const std::function<void(VirtualMachine const&)>& cb)
+  {
+    on_this_migration_start.connect(cb);
+  }
+  /*! \static Add a callback fired when any VM ends a migration*/
   static void on_migration_end_cb(const std::function<void(VirtualMachine const&)>& cb)
   {
     on_migration_end.connect(cb);
   }
+  /*! Add a callback fired when this specific VM ends a migration*/
+  void on_this_migration_end_cb(const std::function<void(VirtualMachine const&)>& cb)
+  {
+    on_this_migration_end.connect(cb);
+  }
 };
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
 
 #endif
index 399d63f..338c337 100644 (file)
@@ -20,9 +20,7 @@ XBT_PUBLIC void simcall_run_blocking(std::function<void()> const& code,
 XBT_PUBLIC void simcall_run_object_access(std::function<void()> const& code,
                                           simgrid::kernel::actor::ObjectAccessSimcallItem* item);
 
-namespace simgrid {
-namespace kernel {
-namespace actor {
+namespace simgrid::kernel::actor {
 
 /** Execute some code in kernel context on behalf of the user code.
  *
@@ -115,8 +113,5 @@ auto simcall_blocking(F&& code, Observer* observer) -> decltype(observer->get_re
   simcall_blocking(std::forward<F>(code), static_cast<SimcallObserver*>(observer));
   return observer->get_result();
 }
-// compact namespaces are C++17 and this is a public header file so let's stick to C++14
-} // namespace actor
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::actor
 #endif
index 97071be..35938c4 100644 (file)
@@ -10,8 +10,7 @@
 #ifdef __cplusplus
 
 #include <boost/intrusive_ptr.hpp>
-namespace simgrid {
-namespace smpi {
+namespace simgrid::smpi {
 
 class Colls;
 class Comm;
@@ -31,8 +30,7 @@ class Topo_Graph;
 class Topo_Dist_Graph;
 class Win;
 
-}
-}
+} // namespace simgrid::smpi
 
 using SMPI_Comm                = simgrid::smpi::Comm;
 using SMPI_Datatype            = simgrid::smpi::Datatype;
index eb205de..e315219 100644 (file)
@@ -13,8 +13,7 @@
 #include <limits>
 #include <vector>
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 template<class T, class U> class Extension;
 template<class T>          class Extendable;
@@ -121,7 +120,6 @@ public:
 
 // Initialized with a first element, to save space for void* user data
 template <class T> std::vector<std::function<void(void*)>> Extendable<T>::deleters_{1};
-}
-}
+} // namespace simgrid::xbt
 
 #endif
index 90ffbea..623a35a 100644 (file)
@@ -10,8 +10,7 @@
 #include <string>
 #include <unordered_map>
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 /** @brief a PropertyHolder can be given a set of textual properties
  *
@@ -32,7 +31,6 @@ public:
   template <class Assoc> void set_properties(const Assoc& properties);
 };
 
-} // namespace xbt
-} // namespace simgrid
+} // namespace simgrid::xbt
 
 #endif
index 8e2e857..18a7d6b 100644 (file)
@@ -11,8 +11,7 @@
 
 #include <xbt/automaton.h>
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 /** Add a proposition to an automaton (the C++ way)
  *
@@ -26,6 +25,5 @@ template <class F> xbt_automaton_propositional_symbol_t add_proposition(const_xb
       a, id, [](auto* cb) -> int { return (*(F*)cb)(); }, callback, [](auto* cb) -> void { delete (F*)cb; });
 }
 
-}
-}
+} // namespace simgrid::xbt
 #endif
index cda0da7..c220965 100644 (file)
@@ -17,8 +17,7 @@ SG_BEGIN_DECL
 XBT_PUBLIC void xbt_backtrace_display_current();
 SG_END_DECL
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 class BacktraceImpl;
 /** A backtrace
@@ -38,6 +37,5 @@ public:
   void display() const;
 };
 
-}
-}
+} // namespace simgrid::xbt
 #endif
index 372ec9b..bb5ee91 100644 (file)
@@ -22,8 +22,7 @@
 #include <xbt/sysdep.h>
 #include <xbt/utility.hpp>
 
-namespace simgrid {
-namespace config {
+namespace simgrid::config {
 
 class Config;
 
@@ -140,7 +139,7 @@ void bind_flag(T& value, const char* name, std::initializer_list<const char*> al
  */
 // F is a checker, F : T& -> ()
 template <class T, class F>
-typename std::enable_if_t<std::is_same<void, decltype(std::declval<F>()(std::declval<const T&>()))>::value, void>
+typename std::enable_if_t<std::is_same_v<void, decltype(std::declval<F>()(std::declval<const T&>()))>, void>
 bind_flag(T& value, const char* name, const char* description, F callback)
 {
   declare_flag(name, description, value, std::function<void(const T&)>([&value, callback](const T& val) {
@@ -150,7 +149,7 @@ bind_flag(T& value, const char* name, const char* description, F callback)
 }
 
 template <class T, class F>
-typename std::enable_if_t<std::is_same<void, decltype(std::declval<F>()(std::declval<const T&>()))>::value, void>
+typename std::enable_if_t<std::is_same_v<void, decltype(std::declval<F>()(std::declval<const T&>()))>, void>
 bind_flag(T& value, const char* name, std::initializer_list<const char*> aliases, const char* description, F callback)
 {
   bind_flag(value, name, description, std::move(callback));
@@ -158,8 +157,7 @@ bind_flag(T& value, const char* name, std::initializer_list<const char*> aliases
 }
 
 template <class F>
-typename std::enable_if_t<std::is_same<void, decltype(std::declval<F>()(std::declval<const std::string&>()))>::value,
-                          void>
+typename std::enable_if_t<std::is_same_v<void, decltype(std::declval<F>()(std::declval<const std::string&>()))>, void>
 bind_flag(std::string& value, const char* name, const char* description,
           const std::map<std::string, std::string, std::less<>>& valid_values, F callback)
 {
@@ -175,14 +173,13 @@ bind_flag(std::string& value, const char* name, const char* description,
                    mesg += std::string("Possible values for option ") + name + ":\n";
                  else
                    mesg += "Invalid value '" + val + "' for option " + name + ". Possible values:\n";
-                 for (auto const& kv : valid_values)
-                   mesg += "  - '" + kv.first + "': " + kv.second + (kv.first == value ? "  <=== DEFAULT" : "") + "\n";
+                 for (auto const& [v, descr] : valid_values)
+                   mesg += "  - '" + v + "': " + descr + (v == value ? "  <=== DEFAULT" : "") + "\n";
                  xbt_die("%s", mesg.c_str());
                }));
 }
 template <class F>
-typename std::enable_if_t<std::is_same<void, decltype(std::declval<F>()(std::declval<const std::string&>()))>::value,
-                          void>
+typename std::enable_if_t<std::is_same_v<void, decltype(std::declval<F>()(std::declval<const std::string&>()))>, void>
 bind_flag(std::string& value, const char* name, std::initializer_list<const char*> aliases, const char* description,
           const std::map<std::string, std::string, std::less<>>& valid_values, F callback)
 {
@@ -199,7 +196,7 @@ bind_flag(std::string& value, const char* name, std::initializer_list<const char
  */
 // F is a predicate, F : T const& -> bool
 template <class T, class F>
-typename std::enable_if_t<std::is_same<bool, decltype(std::declval<F>()(std::declval<const T&>()))>::value, void>
+typename std::enable_if_t<std::is_same_v<bool, decltype(std::declval<F>()(std::declval<const T&>()))>, void>
 bind_flag(T& value, const char* name, const char* description, F callback)
 {
   declare_flag(name, description, value, std::function<void(const T&)>([&value, callback](const T& val) {
@@ -325,7 +322,6 @@ XBT_PUBLIC void finalize();
 XBT_PUBLIC void show_aliases();
 XBT_PUBLIC void help();
 
-} // namespace config
-} // namespace simgrid
+} // namespace simgrid::config
 
 #endif
index d0447d3..afb2622 100644 (file)
@@ -10,8 +10,7 @@
 #include <vector>
 #include <xbt/base.h>
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 void path_push(std::string const& str);
 void path_pop();
@@ -38,6 +37,6 @@ public:
 private:
   std::string path_;
 };
-}}
+} // namespace simgrid::xbt
 
 #endif                          /* XBT_FILE_HPP */
index eea2729..9d1b337 100644 (file)
@@ -23,8 +23,7 @@
 #include <utility>
 #include <vector>
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 template <class F> class MainFunction {
   F code_;
@@ -74,12 +73,12 @@ constexpr auto apply(F&& f, Tuple&& t, std::index_sequence<I...>)
  *  @endcode
  **/
 template <class F, class Tuple>
-constexpr auto apply(F&& f, Tuple&& t) -> decltype(
-    simgrid::xbt::bits::apply(std::forward<F>(f), std::forward<Tuple>(t),
-                              std::make_index_sequence<std::tuple_size<typename std::decay_t<Tuple>>::value>()))
+constexpr auto apply(F&& f, Tuple&& t)
+    -> decltype(simgrid::xbt::bits::apply(std::forward<F>(f), std::forward<Tuple>(t),
+                                          std::make_index_sequence<std::tuple_size_v<typename std::decay_t<Tuple>>>()))
 {
   return simgrid::xbt::bits::apply(std::forward<F>(f), std::forward<Tuple>(t),
-                                   std::make_index_sequence<std::tuple_size<typename std::decay_t<Tuple>>::value>());
+                                   std::make_index_sequence<std::tuple_size_v<typename std::decay_t<Tuple>>>());
 }
 
 template<class T> class Task;
@@ -179,7 +178,7 @@ private:
         return code(std::forward<Args>(args)...);
       },
       // Destroy:
-      std::is_trivially_destructible<F>::value ?
+      std::is_trivially_destructible_v<F> ?
       static_cast<destroy_function>(nullptr) :
       [](TaskUnion& buffer) {
         auto* code = reinterpret_cast<F*>(&buffer);
@@ -260,6 +259,5 @@ template <class F, class... Args> auto make_task(F code, Args... args) -> Task<d
   return Task<decltype(code(std::move(args)...))()>(std::move(task));
 }
 
-} // namespace xbt
-} // namespace simgrid
+} // namespace simgrid::xbt
 #endif
index dabdbbe..e2598ed 100644 (file)
@@ -9,8 +9,7 @@
 #include <simgrid/Exception.hpp>
 #include <xbt/log.h>
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 /** Display information about an exception
  *
@@ -23,7 +22,6 @@ XBT_PUBLIC void log_exception(e_xbt_log_priority_t priority, const char* context
 
 XBT_PUBLIC void install_exception_handler();
 
-} // namespace xbt
-} // namespace simgrid
+} // namespace simgrid::xbt
 
-#endif
\ No newline at end of file
+#endif
index 8926b20..dc19907 100644 (file)
@@ -17,8 +17,7 @@
 #include <utility>
 #include <xbt/ex.h>
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 /** A value or an exception (or nothing)
  *
@@ -126,7 +125,6 @@ template <class P, class F> inline void set_promise(P& promise, F&& future)
 {
   fulfill_promise(promise, [&future] { return std::forward<F>(future).get(); });
 }
-}
-}
+} // namespace simgrid::xbt
 
 #endif
index 00f42ea..a5c4c79 100644 (file)
@@ -12,9 +12,7 @@
 #include <random>
 #include <string>
 
-namespace simgrid {
-namespace xbt {
-namespace random {
+namespace simgrid::xbt::random {
 
 /** A random number generator.
  *
@@ -162,8 +160,6 @@ double exponential(double lambda);
  * @param sd Standard deviation of the normal distribution
  */
 double normal(double mean, double sd);
-} // namespace random
-} // namespace xbt
-} // namespace simgrid
+} // namespace simgrid::xbt::random
 
 #endif
index 99eb0b1..47f021e 100644 (file)
@@ -8,8 +8,7 @@
 
 #include <algorithm>
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 /** Describes a contiguous inclusive-exclusive [a,b) range of values */
 template<class T> class Range {
@@ -27,7 +26,6 @@ public:
   bool contain(T const& x) const { return begin_ <= x && end_ > x; }
 };
 
-}
-}
+} // namespace simgrid::xbt
 
 #endif
index 670d851..e6da188 100644 (file)
@@ -15,8 +15,7 @@
 #include <queue>
 #include <unordered_map>
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 /* To split the file if a unique one is given (specific variable for the other case live in runner()) */
 using ReplayAction = std::vector<std::string>;
 
@@ -26,8 +25,7 @@ using ReplayAction = std::vector<std::string>;
  * xbt_replay_set_tracefile(). If trace_filename is not nullptr, then it's not shared and this trace file is for this
  * actor only */
 XBT_PUBLIC int replay_runner(const char* actor_name, const char* trace_filename = nullptr);
-}
-}
+} // namespace simgrid::xbt
 
 using action_fun = std::function<void(simgrid::xbt::ReplayAction&)>;
 XBT_PUBLIC void xbt_replay_action_register(const char* action_name, const action_fun& function);
index 8b6cf91..70c477f 100644 (file)
@@ -10,8 +10,7 @@
 #include <map>
 #include <utility>
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 template <class S> class signal;
 
@@ -35,15 +34,14 @@ public:
   /** Fire that signal, invoking all callbacks */
   R operator()(P... args) const
   {
-    for (auto const& handler : handlers_)
-      handler.second(args...);
+    for (auto const& [_, callback] : handlers_)
+      callback(args...);
   }
   /** Remove a callback */
   void disconnect(unsigned int id) { handlers_.erase(id); }
   /** Remove all callbacks */
   void disconnect_slots() { handlers_.clear(); }
 };
-}
-}
+} // namespace simgrid::xbt
 
 #endif
index a58d27d..ff9d695 100644 (file)
@@ -12,8 +12,7 @@
 #include <cstdlib>
 #include <string>
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 /** Create a C++ string from a C-style format
  *
@@ -27,6 +26,5 @@ XBT_PUBLIC std::string string_printf(const char* fmt, ...) XBT_ATTRIB_PRINTF(1,
  */
 XBT_PUBLIC std::string string_vprintf(const char* fmt, va_list ap) XBT_ATTRIB_PRINTF(1, 0);
 
-} // namespace xbt
-}
-#endif
\ No newline at end of file
+} // namespace simgrid::xbt
+#endif
index d5620b4..c94eaff 100644 (file)
@@ -11,8 +11,7 @@
 #ifndef SIMGRID_MC_SYSTEM_ERROR_HPP
 #define SIMGRID_MC_SYSTEM_ERROR_HPP
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 /** A `error_category` suitable to be used with `errno`
  *
@@ -82,7 +81,6 @@ std::system_error errno_error(const char* what)
   return std::system_error(errno_code(), what);
 }
 
-}
-}
+} // namespace simgrid::xbt
 
 #endif
index bb599d6..c35be72 100644 (file)
@@ -31,8 +31,7 @@
   }                                                                                                                    \
   enum class EnumType { __VA_ARGS__ } /* defined here to handle trailing semicolon */
 
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
 
 /** @brief Replacement for C++20's std::type_identity_t
  */
@@ -80,6 +79,5 @@ template <class List, class Elem> inline void intrusive_erase(List& list, Elem&
   list.erase(list.iterator_to(elem));
 }
 
-} // namespace xbt
-} // namespace simgrid
+} // namespace simgrid::xbt
 #endif
index 4207696..dc19f99 100644 (file)
@@ -14,7 +14,7 @@ sonar.sources=src,examples,include,teshsuite
 
 
 # Disable some rules on some files
-sonar.issue.ignore.multicriteria=c1,c2a,c2b,c3,c5a,c5b,c6a,c6b,c7,c8,c9,c10a,c10b,c10c,cex1a,cex1b,cex2a,cex2b,cex3,cex4,cxx17a,cxx17b,cxx17c,cxx17d,cxx17e,cxx17f,cxx17g,f1,p1,s1,s2,s3,s4,s5
+sonar.issue.ignore.multicriteria=c1,c2a,c2b,c3,c5a,c5b,c6a,c6b,c7,c8,c9,c10a,c10b,c10c,cex1a,cex1b,cex2a,cex2b,cex3,cex4,f1,p1,s1,s2,s3,s4,s5
 
 # Pointers should not be cast to integral types
 # But we need that for smpi and other places
@@ -95,30 +95,6 @@ sonar.issue.ignore.multicriteria.cex3.resourceKey=examples/**/*.c
 sonar.issue.ignore.multicriteria.cex4.ruleKey=c:S995
 sonar.issue.ignore.multicriteria.cex4.resourceKey=examples/**/*.c
 
-# Ignore these C++17 rules in public headers, where we still support C++14
-
-# C++17: Concise syntax should be used for concatenatable namespaces
-sonar.issue.ignore.multicriteria.cxx17a.ruleKey=cpp:S5812
-sonar.issue.ignore.multicriteria.cxx17a.resourceKey=include/**/*
-# C++17: "if","switch", and range-based for loop initializer should be used to reduce scope of variables
-sonar.issue.ignore.multicriteria.cxx17b.ruleKey=cpp:S6004
-sonar.issue.ignore.multicriteria.cxx17b.resourceKey=include/**/*.hpp
-# C++17: Structured binding should be used
-sonar.issue.ignore.multicriteria.cxx17c.ruleKey=cpp:S6005
-sonar.issue.ignore.multicriteria.cxx17c.resourceKey=include/**/*.hpp
-# C++17: "std::string_view" should be used to pass a read-only string to a function
-sonar.issue.ignore.multicriteria.cxx17d.ruleKey=cpp:S6009
-sonar.issue.ignore.multicriteria.cxx17d.resourceKey=include/**/*.hpp
-# C++17: Redundant class template arguments should not be used
-sonar.issue.ignore.multicriteria.cxx17e.ruleKey=cpp:S6012
-sonar.issue.ignore.multicriteria.cxx17e.resourceKey=include/**/*.hpp
-# C++17: The "_t" and "_v" version of type traits should be used instead of "::type" and "::value"
-sonar.issue.ignore.multicriteria.cxx17f.ruleKey=cpp:S6020
-sonar.issue.ignore.multicriteria.cxx17f.resourceKey=include/**/*.hpp
-# C++17: "std::scoped_lock" should be used instead of "std::lock_guard"
-sonar.issue.ignore.multicriteria.cxx17g.ruleKey=cpp:S5997
-sonar.issue.ignore.multicriteria.cxx17g.resourceKey=include/**/*.hpp
-
 # "reinterpret_cast" should not be used
 # But we need this to interface C and Fortran
 sonar.issue.ignore.multicriteria.f1.ruleKey=cpp:S3630
index ef00ea4..a89056d 100644 (file)
@@ -11,6 +11,7 @@
 #include "simgrid/kernel/ProfileBuilder.hpp"
 #include "simgrid/kernel/routing/NetPoint.hpp"
 #include <simgrid/Exception.hpp>
+#include <simgrid/plugins/task.hpp>
 #include <simgrid/s4u/Actor.hpp>
 #include <simgrid/s4u/Barrier.hpp>
 #include <simgrid/s4u/Comm.hpp>
@@ -18,6 +19,7 @@
 #include <simgrid/s4u/Engine.hpp>
 #include <simgrid/s4u/Exec.hpp>
 #include <simgrid/s4u/Host.hpp>
+#include <simgrid/s4u/Io.hpp>
 #include <simgrid/s4u/Link.hpp>
 #include <simgrid/s4u/Mailbox.hpp>
 #include <simgrid/s4u/Mutex.hpp>
 #include <vector>
 
 namespace py = pybind11;
+using simgrid::plugins::CommTask;
+using simgrid::plugins::CommTaskPtr;
+using simgrid::plugins::ExecTask;
+using simgrid::plugins::ExecTaskPtr;
+using simgrid::plugins::IoTask;
+using simgrid::plugins::IoTaskPtr;
+using simgrid::plugins::Task;
+using simgrid::plugins::TaskPtr;
 using simgrid::s4u::Actor;
 using simgrid::s4u::ActorPtr;
 using simgrid::s4u::Barrier;
 using simgrid::s4u::BarrierPtr;
 using simgrid::s4u::Comm;
 using simgrid::s4u::CommPtr;
+using simgrid::s4u::Disk;
 using simgrid::s4u::Engine;
 using simgrid::s4u::Host;
+using simgrid::s4u::Io;
 using simgrid::s4u::Link;
 using simgrid::s4u::Mailbox;
 using simgrid::s4u::Mutex;
@@ -235,7 +247,7 @@ PYBIND11_MODULE(simgrid, m)
                   params[i - 1] = py::cast(args[i]);
 
                 const auto fun_or_class = py::reinterpret_borrow<py::object>(fun_or_class_p);
-                py::object res = fun_or_class(*params);
+                py::object res          = fun_or_class(*params);
                 /* If I was passed a class, I just built an instance, so I need to call it now */
                 if (py::isinstance<py::function>(res))
                   res();
@@ -310,7 +322,10 @@ PYBIND11_MODULE(simgrid, m)
                              "Retrieve the netpoint associated to this zone")
       .def("seal", &simgrid::s4u::NetZone::seal, "Seal this NetZone")
       .def_property_readonly("name", &simgrid::s4u::NetZone::get_name,
-                             "The name of this network zone (read-only property).");
+                             "The name of this network zone (read-only property).")
+      .def(
+          "__repr__", [](const simgrid::s4u::NetZone net) { return "NetZone(" + net.get_name() + ")"; },
+          "Textual representation of the NetZone");
 
   /* Class ClusterCallbacks */
   py::class_<simgrid::s4u::ClusterCallbacks>(m, "ClusterCallbacks", "Callbacks used to create cluster zones")
@@ -403,6 +418,7 @@ PYBIND11_MODULE(simgrid, m)
              return self.attr("netpoint");
            })
       .def_property_readonly("netpoint", &Host::get_netpoint, "Retrieve the netpoint associated to this zone")
+      .def_property_readonly("disks", &Host::get_disks, "The list of disks on this host (read-only).")
       .def("get_disks", &Host::get_disks, "Retrieve the list of disks in this host")
       .def("set_core_count",
            [](py::object self, double count) // XBT_ATTRIB_DEPRECATED_v334
@@ -457,7 +473,10 @@ PYBIND11_MODULE(simgrid, m)
               }
             });
           },
-          "");
+          "")
+      .def(
+          "__repr__", [](const Host* h) { return "Host(" + h->get_name() + ")"; },
+          "Textual representation of the Host");
 
   py::enum_<simgrid::s4u::Host::SharingPolicy>(host, "SharingPolicy")
       .value("NONLINEAR", simgrid::s4u::Host::SharingPolicy::NONLINEAR)
@@ -479,7 +498,10 @@ PYBIND11_MODULE(simgrid, m)
            "Set sharing policy for this disk", py::arg("op"), py::arg("policy"),
            py::arg("cb") = simgrid::s4u::NonLinearResourceCb())
       .def("seal", &simgrid::s4u::Disk::seal, py::call_guard<py::gil_scoped_release>(), "Seal this disk")
-      .def_property_readonly("name", &simgrid::s4u::Disk::get_name, "The name of this disk (read-only property).");
+      .def_property_readonly("name", &simgrid::s4u::Disk::get_name, "The name of this disk (read-only property).")
+      .def(
+          "__repr__", [](const Disk* d) { return "Disk(" + d->get_name() + ")"; },
+          "Textual representation of the Disk");
   py::enum_<simgrid::s4u::Disk::SharingPolicy>(disk, "SharingPolicy")
       .value("NONLINEAR", simgrid::s4u::Disk::SharingPolicy::NONLINEAR)
       .value("LINEAR", simgrid::s4u::Disk::SharingPolicy::LINEAR)
@@ -575,8 +597,10 @@ PYBIND11_MODULE(simgrid, m)
       .def_property_readonly("name", &Link::get_name, "The name of this link")
       .def_property_readonly("bandwidth", &Link::get_bandwidth,
                              "The bandwidth (in bytes per second) (read-only property).")
-      .def_property_readonly("latency", &Link::get_latency, "The latency (in seconds) (read-only property).");
-
+      .def_property_readonly("latency", &Link::get_latency, "The latency (in seconds) (read-only property).")
+      .def(
+          "__repr__", [](const Link* l) { return "Link(" + l->get_name() + ")"; },
+          "Textual representation of the Link");
   py::enum_<Link::SharingPolicy>(link, "SharingPolicy")
       .value("NONLINEAR", Link::SharingPolicy::NONLINEAR)
       .value("WIFI", Link::SharingPolicy::WIFI)
@@ -619,8 +643,8 @@ PYBIND11_MODULE(simgrid, m)
   py::class_<simgrid::s4u::Mailbox, std::unique_ptr<Mailbox, py::nodelete>>(
       m, "Mailbox", "Mailbox. See the C++ documentation for details.")
       .def(
-          "__str__", [](const Mailbox* self) { return "Mailbox(" + self->get_name() + ")"; },
-          "Textual representation of the Mailbox`")
+          "__repr__", [](const Mailbox* self) { return "Mailbox(" + self->get_name() + ")"; },
+          "Textual representation of the Mailbox")
       .def_static("by_name", &Mailbox::by_name, py::call_guard<py::gil_scoped_release>(), py::arg("name"),
                   "Retrieve a Mailbox from its name")
       .def_property_readonly("name", &Mailbox::get_name, "The name of that mailbox (read-only property).")
@@ -794,8 +818,7 @@ PYBIND11_MODULE(simgrid, m)
       .def("acquire_timeout", &Semaphore::acquire_timeout, py::call_guard<py::gil_scoped_release>(), py::arg("timeout"),
            "Acquire on the semaphore object with no timeout. Blocks until the semaphore is acquired or return "
            "true if it has not been acquired after the specified timeout.")
-      .def("release", &Semaphore::release, py::call_guard<py::gil_scoped_release>(),
-           "Release the semaphore.")
+      .def("release", &Semaphore::release, py::call_guard<py::gil_scoped_release>(), "Release the semaphore.")
       .def_property_readonly("capacity", &Semaphore::get_capacity, py::call_guard<py::gil_scoped_release>(),
                              "Get the semaphore capacity.")
       .def_property_readonly("would_block", &Semaphore::would_block, py::call_guard<py::gil_scoped_release>(),
@@ -817,12 +840,12 @@ PYBIND11_MODULE(simgrid, m)
       .def("unlock", &Mutex::unlock, py::call_guard<py::gil_scoped_release>(), "Release the mutex.")
       // Allow mutexes to be automatically acquired/released with a context manager: `with mutex: ...`
       .def("__enter__", &Mutex::lock, py::call_guard<py::gil_scoped_release>())
-      .def("__exit__", [](Mutex* self, const py::object&, const py::object&, const py::object&) { self->unlock(); },
-           py::call_guard<py::gil_scoped_release>());
+      .def(
+          "__exit__", [](Mutex* self, const py::object&, const py::object&, const py::object&) { self->unlock(); },
+          py::call_guard<py::gil_scoped_release>());
 
   /* Class Barrier */
-  py::class_<Barrier, BarrierPtr>(m, "Barrier",
-                                  "A classical barrier, but blocking in the simulation world.")
+  py::class_<Barrier, BarrierPtr>(m, "Barrier", "A classical barrier, but blocking in the simulation world.")
       .def(py::init<>(&Barrier::create), py::call_guard<py::gil_scoped_release>(), py::arg("expected_actors"),
            "Barrier constructor.")
       .def("wait", &Barrier::wait, py::call_guard<py::gil_scoped_release>(),
@@ -886,5 +909,100 @@ PYBIND11_MODULE(simgrid, m)
       .def("resume", &Actor::resume, py::call_guard<py::gil_scoped_release>(),
            "Resume that actor, that was previously suspend()ed.")
       .def_static("kill_all", &Actor::kill_all, py::call_guard<py::gil_scoped_release>(),
-                  "Kill all actors but the caller.");
+                  "Kill all actors but the caller.")
+      .def(
+          "__repr__", [](const ActorPtr a) { return "Actor(" + a->get_name() + ")"; },
+          "Textual representation of the Actor");
+
+  /* Enum Class IoOpType */
+  py::enum_<simgrid::s4u::Io::OpType>(m, "IoOpType")
+      .value("READ", simgrid::s4u::Io::OpType::READ)
+      .value("WRITE", simgrid::s4u::Io::OpType::WRITE);
+
+  /* Class Task */
+  py::class_<Task, TaskPtr>(m, "Task", "Task. See the C++ documentation for details.")
+      .def_static("init", &Task::init)
+      .def_static(
+          "on_start_cb",
+          [](py::object cb) {
+            cb.inc_ref(); // keep alive after return
+            const py::gil_scoped_release gil_release;
+            Task::on_start_cb([cb](Task* op) {
+              const py::gil_scoped_acquire py_context; // need a new context for callback
+              py::reinterpret_borrow<py::function>(cb.ptr())(op);
+            });
+          },
+          "Add a callback called when each task starts.")
+      .def_static(
+          "on_end_cb",
+          [](py::object cb) {
+            cb.inc_ref(); // keep alive after return
+            const py::gil_scoped_release gil_release;
+            Task::on_end_cb([cb](Task* op) {
+              const py::gil_scoped_acquire py_context; // need a new context for callback
+              py::reinterpret_borrow<py::function>(cb.ptr())(op);
+            });
+          },
+          "Add a callback called when each task ends.")
+      .def_property_readonly("name", &Task::get_name, "The name of this task (read-only).")
+      .def_property_readonly("count", &Task::get_count, "The execution count of this task (read-only).")
+      .def_property_readonly("successors", &Task::get_successors, "The successors of this task (read-only).")
+      .def_property("amount", &Task::get_amount, &Task::set_amount, "The amount of work to do for this task.")
+      .def("enqueue_execs", py::overload_cast<int>(&Task::enqueue_execs), py::call_guard<py::gil_scoped_release>(),
+           py::arg("n"), "Enqueue executions for this task.")
+      .def("add_successor", py::overload_cast<TaskPtr>(&Task::add_successor), py::call_guard<py::gil_scoped_release>(),
+           py::arg("op"), "Add a successor to this task.")
+      .def("remove_successor", py::overload_cast<TaskPtr>(&Task::remove_successor),
+           py::call_guard<py::gil_scoped_release>(), py::arg("op"), "Remove a successor of this task.")
+      .def("remove_all_successors", &Task::remove_all_successors, py::call_guard<py::gil_scoped_release>(),
+           "Remove all successors of this task.")
+      .def("on_this_start", py::overload_cast<const std::function<void(Task*)>&>(&Task::on_this_start), py::arg("func"),
+           "Add a callback called when this task starts.")
+      .def("on_this_end", py::overload_cast<const std::function<void(Task*)>&>(&Task::on_this_end), py::arg("func"),
+           "Add a callback called when this task ends.")
+      .def(
+          "__repr__", [](const TaskPtr op) { return "Task(" + op->get_name() + ")"; },
+          "Textual representation of the Task");
+
+  /* Class CommTask */
+  py::class_<CommTask, CommTaskPtr, Task>(m, "CommTask", "Communication Task. See the C++ documentation for details.")
+      .def_static("init", py::overload_cast<const std::string&>(&CommTask::init),
+                  py::call_guard<py::gil_scoped_release>(), py::arg("name"), "CommTask constructor")
+      .def_static("init", py::overload_cast<const std::string&, double, Host*, Host*>(&CommTask::init),
+                  py::call_guard<py::gil_scoped_release>(), py::arg("name"), py::arg("bytes"), py::arg("source"),
+                  py::arg("destination"), "CommTask constructor")
+      .def_property("source", &CommTask::get_source, &CommTask::set_source, "The source of the communication.")
+      .def_property("destination", &CommTask::get_destination, &CommTask::set_destination,
+                    "The destination of the communication.")
+      .def_property("bytes", &CommTask::get_bytes, &CommTask::set_bytes, "The amount of bytes to send.")
+      .def(
+          "__repr__", [](const CommTaskPtr c) { return "CommTask(" + c->get_name() + ")"; },
+          "Textual representation of the CommTask");
+
+  /* Class ExecTask */
+  py::class_<ExecTask, ExecTaskPtr, Task>(m, "ExecTask", "Execution Task. See the C++ documentation for details.")
+      .def_static("init", py::overload_cast<const std::string&>(&ExecTask::init),
+                  py::call_guard<py::gil_scoped_release>(), py::arg("name"), "ExecTask constructor")
+      .def_static("init", py::overload_cast<const std::string&, double, Host*>(&ExecTask::init),
+                  py::call_guard<py::gil_scoped_release>(), py::arg("name"), py::arg("flops"), py::arg("host"),
+                  "CommTask constructor.")
+      .def_property("host", &ExecTask::get_host, &ExecTask::set_host, "The host of the execution.")
+      .def_property("flops", &ExecTask::get_flops, &ExecTask::set_flops, "The amount of flops to execute.")
+      .def(
+          "__repr__", [](const ExecTaskPtr e) { return "ExecTask(" + e->get_name() + ")"; },
+          "Textual representation of the ExecTask");
+
+  /* Class IoTask */
+  py::class_<IoTask, IoTaskPtr, Task>(m, "IoTask", "IO Task. See the C++ documentation for details.")
+      .def_static("init", py::overload_cast<const std::string&>(&IoTask::init),
+                  py::call_guard<py::gil_scoped_release>(), py::arg("name"), "IoTask constructor")
+      .def_static("init", py::overload_cast<const std::string&, double, Disk*, Io::OpType>(&IoTask::init),
+                  py::call_guard<py::gil_scoped_release>(), py::arg("name"), py::arg("bytes"), py::arg("disk"),
+                  py::arg("type"), "IoTask constructor.")
+      .def_property("disk", &IoTask::get_disk, &IoTask::set_disk, "The disk of the IO.")
+      .def_property("bytes", &IoTask::get_bytes, &IoTask::set_bytes, "The amount of bytes to process.")
+      .def_property("type", &IoTask::get_bytes, &IoTask::set_bytes, "The type of IO.")
+      .def(
+          "__repr__", [](const IoTaskPtr io) { return "IoTask(" + io->get_name() + ")"; },
+          "Textual representation of the IoTask");
 }
index f0d1826..218aa71 100644 (file)
 #include "dax_dtd.c"
 
 #if SIMGRID_HAVE_JSON
+// Disable implicit conversions. See https://github.com/nlohmann/json#implicit-conversions
+#ifdef JSON_USE_IMPLICIT_CONVERSIONS
+#undef JSON_USE_IMPLICIT_CONVERSIONS
+#endif
+#define JSON_USE_IMPLICIT_CONVERSIONS 0
 #include <nlohmann/json.hpp>
 #include <sstream>
 #endif
@@ -101,14 +106,19 @@ std::vector<ActivityPtr> create_DAG_from_json(const std::string& filename)
   
   for (auto const& task: data["workflow"]["tasks"]) {
     if (task["type"] == "compute") {
-      current = Exec::init()->set_name(task["name"])->set_flops_amount(task["runtime"]);
+      current =
+          Exec::init()->set_name(task["name"].get<std::string>())->set_flops_amount(task["runtime"].get<double>());
       if (task.contains("machine"))
-        dynamic_cast<Exec*>(current.get())->set_host(simgrid::s4u::Engine::get_instance()->host_by_name(task["machine"]));
+        dynamic_cast<Exec*>(current.get())
+            ->set_host(simgrid::s4u::Engine::get_instance()->host_by_name(task["machine"].get<std::string>()));
     }
     else if (task["type"] == "transfer"){
-      current = Comm::sendto_init()->set_name(task["name"])->set_payload_size(task["bytesWritten"]);
+      current = Comm::sendto_init()
+                    ->set_name(task["name"].get<std::string>())
+                    ->set_payload_size(task["bytesWritten"].get<double>());
       if (task.contains("machine"))
-        comms_destinations[current] = simgrid::s4u::Engine::get_instance()->host_by_name(task["machine"]);
+        comms_destinations[current] =
+            simgrid::s4u::Engine::get_instance()->host_by_name(task["machine"].get<std::string>());
       if (task["parents"].size() == 1) {
         ActivityPtr parent_activity;
         for (auto const& activity: dag) {
@@ -130,7 +140,7 @@ std::vector<ActivityPtr> create_DAG_from_json(const std::string& filename)
 
     dag.push_back(current);
     for (auto const& parent : task["parents"])
-      successors[parent].push_back(current);
+      successors[parent.get<std::string>()].push_back(current);
   }
   // Assign successors
   for (auto const& [parent, successors_list] : successors)
index 694a04a..94b95e3 100644 (file)
@@ -14,6 +14,7 @@
 #include <xbt/graph.h>
 
 #include "src/instr/instr_private.hpp"
+#include "src/kernel/activity/ExecImpl.hpp"
 #include "src/kernel/resource/CpuImpl.hpp"
 #include "src/kernel/resource/NetworkModel.hpp"
 
@@ -347,6 +348,15 @@ static void on_host_creation(s4u::Host const& host)
     root->get_type()->by_name_or_create("MIGRATE_LINK", mpi, mpi);
     mpi->by_name_or_create<StateType>("MIGRATE_STATE");
   }
+
+   if (TRACE_actor_is_enabled()) {
+    auto* host_type = container->get_type();
+    auto* state     = host_type->by_name_or_create<StateType>("HOST_STATE");
+    state->set_calling_container(container);
+    state->add_entity_value("receive", "1 0 0");
+    state->add_entity_value("send", "0 0 1");
+    state->add_entity_value("execute", "0 1 1");
+  }
 }
 
 static void on_action_state_change(kernel::resource::Action const& action,
@@ -362,12 +372,17 @@ static void on_action_state_change(kernel::resource::Action const& action,
       resource_set_utilization("HOST", "speed_used", cpu->get_cname(), action.get_category(), value,
                                action.get_last_update(), simgrid_get_clock() - action.get_last_update());
 
-    if (const auto* link = dynamic_cast<kernel::resource::StandardLinkImpl*>(resource))
+    else if (const auto* link = dynamic_cast<kernel::resource::StandardLinkImpl*>(resource))
       resource_set_utilization("LINK", "bandwidth_used", link->get_cname(), action.get_category(), value,
                                action.get_last_update(), simgrid_get_clock() - action.get_last_update());
   }
 }
 
+static void on_activity_suspend_resume(s4u::Activity const& activity)
+{
+  on_action_state_change(*activity.get_impl()->model_action_, /*ignored*/ kernel::resource::Action::State::STARTED);
+}
+
 static void on_platform_created()
 {
   currentContainer.clear();
@@ -462,8 +477,10 @@ void define_callbacks()
 
   s4u::NetZone::on_creation_cb(on_netzone_creation);
 
-  kernel::resource::CpuAction::on_state_change.connect(on_action_state_change);
+  s4u::Host::on_exec_state_change_cb(on_action_state_change);
   s4u::Link::on_communication_state_change_cb(on_action_state_change);
+  s4u::Exec::on_suspend_cb(on_activity_suspend_resume);
+  s4u::Exec::on_resume_cb(on_activity_suspend_resume);
 
   if (TRACE_actor_is_enabled()) {
     s4u::Actor::on_creation_cb(on_actor_creation);
@@ -482,16 +499,43 @@ void define_callbacks()
     });
     s4u::Actor::on_wake_up_cb(
         [](s4u::Actor const& actor) { Container::by_name(instr_pid(actor))->get_state("ACTOR_STATE")->pop_event(); });
-    s4u::Exec::on_start_cb([](s4u::Exec const&) {
-      Container::by_name(instr_pid(*s4u::Actor::self()))->get_state("ACTOR_STATE")->push_event("execute");
+
+    s4u::Exec::on_start_cb([](s4u::Exec const& e) {
+      std::string pid = instr_pid(*s4u::Actor::self());
+      if (pid == "-0") //Exec is launched directly by Maestro, use the host as container
+        Container::by_name(e.get_host()->get_name())->get_state("HOST_STATE")->push_event("execute");
+      else
+        Container::by_name(pid)->get_state("ACTOR_STATE")->push_event("execute");
+    });
+
+    s4u::Exec::on_completion_cb([](const s4u::Exec& e) {
+      std::string pid = instr_pid(*s4u::Actor::self());
+      if (pid == "-0") //Exec is launched directly by Maestro, use the host as container
+        Container::by_name(e.get_host()->get_name())->get_state("HOST_STATE")->pop_event();
+      else
+        Container::by_name(pid)->get_state("ACTOR_STATE")->pop_event();
+    });
+
+    s4u::Comm::on_completion_cb([](const s4u::Comm& c) {
+      if (c.get_sender()) {
+        Container::by_name(instr_pid(*c.get_sender()))->get_state("ACTOR_STATE")->pop_event();
+        Container::by_name(instr_pid(*c.get_receiver()))->get_state("ACTOR_STATE")->pop_event();
+      } else {
+        Container::by_name(c.get_source()->get_name())->get_state("HOST_STATE")->pop_event();
+        Container::by_name(c.get_destination()->get_name())->get_state("HOST_STATE")->pop_event();
+      }
     });
-    s4u::Activity::on_completion_cb([](const s4u::Activity&) {
-      Container::by_name(instr_pid(*s4u::Actor::self()))->get_state("ACTOR_STATE")->pop_event();
+    s4u::Comm::on_start_cb([](s4u::Comm const& c) {
+      std::string pid = instr_pid(*s4u::Actor::self());
+      if (pid == "-0") { //Comm is launched directly by Maestro, use the host as container
+        Container::by_name(c.get_source()->get_name())->get_state("HOST_STATE")->push_event("start");
+        Container::by_name(c.get_destination()->get_name())->get_state("HOST_STATE")->push_event("start");
+      }
     });
-    s4u::Comm::on_send_cb([](s4u::Comm const&) {
+    s4u::Comm::on_send_cb([](s4u::Comm const& c) {
       Container::by_name(instr_pid(*s4u::Actor::self()))->get_state("ACTOR_STATE")->push_event("send");
     });
-    s4u::Comm::on_recv_cb([](s4u::Comm const&) {
+    s4u::Comm::on_recv_cb([](s4u::Comm const& c) {
       Container::by_name(instr_pid(*s4u::Actor::self()))->get_state("ACTOR_STATE")->push_event("receive");
     });
     s4u::Actor::on_host_change_cb(on_actor_host_change);
@@ -503,7 +547,7 @@ void define_callbacks()
           ->get_state("MPI_STATE")
           ->push_event("computing", new CpuTIData("compute", exec.get_cost()));
     });
-    s4u::Activity::on_completion_cb([](const s4u::Activity&) {
+    s4u::Exec::on_completion_cb([](const s4u::Exec&) {
       Container::by_name("rank-" + std::to_string(s4u::Actor::self()->get_pid()))->get_state("MPI_STATE")->pop_event();
     });
   }
index f425dc8..a81face 100644 (file)
@@ -176,11 +176,14 @@ void ActivityImpl::wait_any_for(actor::ActorImpl* issuer, const std::vector<Acti
 
 void ActivityImpl::suspend()
 {
-  if (model_action_ == nullptr)
+  if (model_action_ == nullptr) {
+    XBT_CRITICAL("POUET");
     return;
+  }
   XBT_VERB("This activity is suspended (remain: %f)", model_action_->get_remains());
+  get_iface()->fire_on_suspend();
+  get_iface()->fire_on_this_suspend();
   model_action_->suspend();
-  s4u::Activity::on_suspended(*get_iface());
 }
 
 void ActivityImpl::resume()
@@ -188,8 +191,9 @@ void ActivityImpl::resume()
   if (model_action_ == nullptr)
     return;
   XBT_VERB("This activity is resumed (remain: %f)", model_action_->get_remains());
+  get_iface()->fire_on_resume();
+  get_iface()->fire_on_this_resume();
   model_action_->resume();
-  s4u::Activity::on_resumed(*get_iface());
 }
 
 void ActivityImpl::cancel()
index 44ec50b..5dedf54 100644 (file)
@@ -20,8 +20,6 @@
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(ker_network, kernel, "Kernel network-related synchronization");
 
 namespace simgrid::kernel::activity {
-xbt::signal<void(CommImpl const&)> CommImpl::on_start;
-xbt::signal<void(CommImpl const&)> CommImpl::on_completion;
 
 std::function<void(CommImpl*, void*, size_t)> CommImpl::copy_data_callback_ = [](kernel::activity::CommImpl* comm,
                                                                                  void* buff, size_t buff_size) {
@@ -133,7 +131,6 @@ CommImpl* CommImpl::start()
     model_action_->set_category(get_tracing_category());
     set_start_time(model_action_->get_start_time());
     set_state(State::RUNNING);
-    on_start(*this);
 
     XBT_DEBUG("Starting communication %p from '%s' to '%s' (model action: %p; state: %s)", this, from_->get_cname(),
               to_->get_cname(), model_action_, get_state_str());
@@ -471,7 +468,11 @@ void CommImpl::finish()
   XBT_DEBUG("CommImpl::finish() comm %p, state %s, src_proc %p, dst_proc %p, detached: %d", this, get_state_str(),
             src_actor_.get(), dst_actor_.get(), detached_);
 
-  on_completion(*this);
+  if (get_iface()) {
+    const auto& piface = static_cast<const s4u::Comm&>(*get_iface());
+    s4u::Comm::on_completion(piface);
+    piface.on_this_completion(piface);
+  }
 
   /* Update synchro state */
   if (src_timeout_ && src_timeout_->get_state() == resource::Action::State::FINISHED)
index 32fef45..340a19d 100644 (file)
@@ -94,8 +94,6 @@ expectations of the other side, too. See  */
 
   void* src_data_ = nullptr; /* User data associated to the communication */
   void* dst_data_ = nullptr;
-  static xbt::signal<void(CommImpl const&)> on_start;
-  static xbt::signal<void(CommImpl const&)> on_completion;
 };
 } // namespace simgrid::kernel::activity
 
index c2e2b48..4f5fb24 100644 (file)
@@ -44,8 +44,10 @@ ActorImpl::ActorImpl(const std::string& name, s4u::Host* host, aid_t ppid)
 
 ActorImpl::~ActorImpl()
 {
-  if (EngineImpl::has_instance() && not EngineImpl::get_instance()->is_maestro(this))
+  if (EngineImpl::has_instance() && not EngineImpl::get_instance()->is_maestro(this)) {
     s4u::Actor::on_destruction(*get_ciface());
+    get_ciface()->on_this_destruction(*get_ciface());
+  }
 }
 
 /* Become an actor in the simulation
@@ -129,6 +131,7 @@ void ActorImpl::cleanup_from_kernel()
 
   undaemonize();
   s4u::Actor::on_termination(*get_ciface());
+  get_ciface()->on_this_termination(*get_ciface());
 
   while (not mailboxes_.empty())
     mailboxes_.back()->set_receiver(nullptr);
index 84c317e..c065e2a 100644 (file)
@@ -68,7 +68,7 @@ static std::string to_string_activity_test(const activity::ActivityImpl* act)
 void ActivityTestanySimcall::serialize(std::stringstream& stream) const
 {
   stream << (short)mc::Transition::Type::TESTANY << ' ' << activities_.size() << ' ';
-  for (auto const& act : activities_) {
+  for (auto const* act : activities_) {
     serialize_activity_test(act, stream);
     stream << ' ';
   }
@@ -76,7 +76,7 @@ void ActivityTestanySimcall::serialize(std::stringstream& stream) const
 std::string ActivityTestanySimcall::to_string() const
 {
   std::stringstream buffer("TestAny(");
-  for (auto const& act : activities_) {
+  for (auto const* act : activities_) {
     buffer << to_string_activity_test(act);
   }
   return buffer.str();
@@ -127,7 +127,7 @@ void ActivityWaitSimcall::serialize(std::stringstream& stream) const
 void ActivityWaitanySimcall::serialize(std::stringstream& stream) const
 {
   stream << (short)mc::Transition::Type::WAITANY << ' ' << activities_.size() << ' ';
-  for (auto const& act : activities_) {
+  for (auto const* act : activities_) {
     serialize_activity_wait(act, timeout_ > 0, stream);
     stream << ' ';
   }
@@ -139,7 +139,7 @@ std::string ActivityWaitSimcall::to_string() const
 std::string ActivityWaitanySimcall::to_string() const
 {
   std::stringstream buffer("WaitAny(");
-  for (auto const& act : activities_) {
+  for (auto const* act : activities_) {
     buffer << to_string_activity_wait(act);
   }
   return buffer.str();
index a754fd5..d6950bf 100644 (file)
@@ -85,7 +85,7 @@ template <typename C> std::string BmfSolver::debug_vector(const C& container) co
 {
   std::stringstream debug;
   std::copy(container.begin(), container.end(),
-            std::ostream_iterator<typename std::remove_reference<decltype(container)>::type::value_type>(debug, " "));
+            std::ostream_iterator<typename std::remove_reference_t<decltype(container)>::value_type>(debug, " "));
   return debug.str();
 }
 
index d597356..313b3fc 100644 (file)
@@ -98,6 +98,7 @@ double CpuImpl::get_pstate_peak_speed(unsigned long pstate_index) const
 void CpuImpl::on_speed_change()
 {
   s4u::Host::on_speed_change(*piface_);
+  piface_->on_this_speed_change(*piface_);
 }
 
 CpuImpl* CpuImpl::set_core_count(int core_count)
@@ -193,27 +194,11 @@ void CpuAction::update_remains_lazy(double now)
   set_last_value(get_rate());
 }
 
-xbt::signal<void(CpuAction const&, Action::State)> CpuAction::on_state_change;
-
-void CpuAction::suspend()
-{
-  Action::State previous = get_state();
-  on_state_change(*this, previous);
-  Action::suspend();
-}
-
-void CpuAction::resume()
-{
-  Action::State previous = get_state();
-  on_state_change(*this, previous);
-  Action::resume();
-}
-
 void CpuAction::set_state(Action::State state)
 {
   Action::State previous = get_state();
   Action::set_state(state);
-  on_state_change(*this, previous);
+  s4u::Host::on_exec_state_change(*this, previous);
 }
 
 /** @brief returns a list of all CPUs that this action is using */
index 6237702..ae35bd5 100644 (file)
@@ -180,18 +180,10 @@ class XBT_PUBLIC CpuAction : public Action {
 public:
   using Action::Action;
 
-  /** @brief Signal emitted when the action state changes (ready/running/done, etc)
-   *  Signature: `void(CpuAction const& action, simgrid::kernel::resource::Action::State previous)`
-   */
-  static xbt::signal<void(CpuAction const&, Action::State)> on_state_change;
-
   void set_state(Action::State state) override;
 
   void update_remains_lazy(double now) override;
   std::list<CpuImpl*> cpus() const;
-
-  void suspend() override;
-  void resume() override;
 };
 } // namespace simgrid::kernel::resource
 
index 1903b81..a231432 100644 (file)
@@ -53,6 +53,7 @@ DiskImpl* DiskImpl::set_write_constraint(lmm::Constraint* constraint_write)
 void DiskImpl::destroy()
 {
   s4u::Disk::on_destruction(piface_);
+  piface_.on_this_destruction(piface_);
   delete this;
 }
 
@@ -60,14 +61,16 @@ void DiskImpl::turn_on()
 {
   if (not is_on()) {
     Resource::turn_on();
-    s4u::Disk::on_state_change(piface_);
+    s4u::Disk::on_onoff(piface_);
+    piface_.on_this_onoff(piface_);
   }
 }
 void DiskImpl::turn_off()
 {
   if (is_on()) {
     Resource::turn_off();
-    s4u::Disk::on_state_change(piface_);
+    s4u::Disk::on_onoff(piface_);
+    piface_.on_this_onoff(piface_);
 
     const kernel::lmm::Element* elem = nullptr;
     double now                       = EngineImpl::get_clock();
index 4dd7089..e72375a 100644 (file)
@@ -67,6 +67,7 @@ HostImpl::~HostImpl()
 void HostImpl::destroy()
 {
   s4u::Host::on_destruction(*this->get_iface());
+  this->get_iface()->on_this_destruction(*this->get_iface());
   delete this;
 }
 
index 59a07d1..a7661a1 100644 (file)
@@ -37,6 +37,7 @@ void StandardLinkImpl::Deleter::operator()(resource::StandardLinkImpl* link) con
 void StandardLinkImpl::destroy()
 {
   s4u::Link::on_destruction(piface_);
+  piface_.on_this_destruction(piface_);
   delete this;
 }
 
@@ -81,7 +82,8 @@ void StandardLinkImpl::turn_on()
 {
   if (not is_on()) {
     Resource::turn_on();
-    s4u::Link::on_state_change(piface_);
+    s4u::Link::on_onoff(piface_);
+    piface_.on_this_onoff(piface_);
   }
 }
 
@@ -89,7 +91,8 @@ void StandardLinkImpl::turn_off()
 {
   if (is_on()) {
     Resource::turn_off();
-    s4u::Link::on_state_change(piface_);
+    s4u::Link::on_onoff(piface_);
+    piface_.on_this_onoff(piface_);
 
     const kernel::lmm::Element* elem = nullptr;
     double now                       = EngineImpl::get_clock();
@@ -115,6 +118,7 @@ void StandardLinkImpl::seal()
 void StandardLinkImpl::on_bandwidth_change() const
 {
   s4u::Link::on_bandwidth_change(piface_);
+  piface_.on_this_bandwidth_change(piface_);
 }
 
 void StandardLinkImpl::set_bandwidth_profile(profile::Profile* profile)
index 1cf155f..b01f733 100644 (file)
@@ -55,15 +55,15 @@ std::deque<s4u::VirtualMachine*> VirtualMachineImpl::allVms_;
  */
 const double virt_overhead = 1; // 0.95
 
-static void host_state_change(s4u::Host const& host)
+static void host_onoff(s4u::Host const& host)
 {
   if (not host.is_on()) { // just turned off.
     std::vector<s4u::VirtualMachine*> trash;
     /* Find all VMs living on that host */
-    for (s4u::VirtualMachine* const& vm : VirtualMachineImpl::allVms_)
+    for (auto* vm : VirtualMachineImpl::allVms_)
       if (vm->get_pm() == &host)
         trash.push_back(vm);
-    for (s4u::VirtualMachine* vm : trash)
+    for (auto* vm : trash)
       vm->shutdown();
   }
 }
@@ -79,17 +79,14 @@ static void add_active_exec(s4u::Exec const& task)
   }
 }
 
-static void remove_active_exec(s4u::Activity const& task)
+static void remove_active_exec(s4u::Exec const& exec)
 {
-  const auto* exec = dynamic_cast<s4u::Exec const*>(&task);
-  if (exec == nullptr)
+  if (not exec.is_assigned())
     return;
-  if (not exec->is_assigned())
-    return;
-  const s4u::VirtualMachine* vm = dynamic_cast<s4u::VirtualMachine*>(exec->get_host());
+  const s4u::VirtualMachine* vm = dynamic_cast<s4u::VirtualMachine*>(exec.get_host());
   if (vm != nullptr) {
     VirtualMachineImpl* vm_impl = vm->get_vm_impl();
-    for (int i = 1; i <= exec->get_thread_count(); i++)
+    for (int i = 1; i <= exec.get_thread_count(); i++)
       vm_impl->remove_active_exec();
     vm_impl->update_action_weight();
   }
@@ -123,11 +120,11 @@ static void remove_active_activity(s4u::Activity const& act)
 
 VMModel::VMModel(const std::string& name) : HostModel(name)
 {
-  s4u::Host::on_state_change_cb(host_state_change);
+  s4u::Host::on_onoff_cb(host_onoff);
   s4u::Exec::on_start_cb(add_active_exec);
-  s4u::Activity::on_completion_cb(remove_active_exec);
-  s4u::Activity::on_resumed_cb(add_active_activity);
-  s4u::Activity::on_suspended_cb(remove_active_activity);
+  s4u::Exec::on_completion_cb(remove_active_exec);
+  s4u::Exec::on_resume_cb(add_active_activity);
+  s4u::Exec::on_suspend_cb(remove_active_activity);
 }
 
 double VMModel::next_occurring_event(double now)
@@ -157,7 +154,7 @@ double VMModel::next_occurring_event(double now)
    **/
 
   /* iterate for all virtual machines */
-  for (s4u::VirtualMachine* const& ws_vm : VirtualMachineImpl::allVms_) {
+  for (auto const* ws_vm : VirtualMachineImpl::allVms_) {
     if (ws_vm->get_state() == s4u::VirtualMachine::State::SUSPENDED) // Ignore suspended VMs
       continue;
 
@@ -239,6 +236,7 @@ void VirtualMachineImpl::vm_destroy()
 void VirtualMachineImpl::start()
 {
   s4u::VirtualMachine::on_start(*get_iface());
+  get_iface()->on_this_start(*get_iface());
   s4u::VmHostExt::ensureVmExtInstalled();
 
   if (physical_host_->extension<s4u::VmHostExt>() == nullptr)
@@ -249,7 +247,7 @@ void VirtualMachineImpl::start()
       not physical_host_->extension<s4u::VmHostExt>()->overcommit) { /* Need to verify that we don't overcommit */
     /* Retrieve the memory occupied by the VMs on that host. Yep, we have to traverse all VMs of all hosts for that */
     size_t total_ramsize_of_vms = 0;
-    for (auto* const& ws_vm : allVms_)
+    for (auto const* ws_vm : allVms_)
       if (physical_host_ == ws_vm->get_pm())
         total_ramsize_of_vms += ws_vm->get_ramsize();
 
@@ -264,11 +262,13 @@ void VirtualMachineImpl::start()
   vm_state_ = s4u::VirtualMachine::State::RUNNING;
 
   s4u::VirtualMachine::on_started(*get_iface());
+  get_iface()->on_this_started(*get_iface());
 }
 
 void VirtualMachineImpl::suspend(const actor::ActorImpl* issuer)
 {
   s4u::VirtualMachine::on_suspend(*get_iface());
+  get_iface()->on_this_suspend(*get_iface());
 
   if (vm_state_ != s4u::VirtualMachine::State::RUNNING)
     throw VmFailureException(XBT_THROW_POINT,
@@ -308,6 +308,7 @@ void VirtualMachineImpl::resume()
 
   vm_state_ = s4u::VirtualMachine::State::RUNNING;
   s4u::VirtualMachine::on_resume(*get_iface());
+  get_iface()->on_this_resume(*get_iface());
 }
 
 /** @brief Power off a VM.
@@ -334,6 +335,7 @@ void VirtualMachineImpl::shutdown(actor::ActorImpl* issuer)
   set_state(s4u::VirtualMachine::State::DESTROYED);
 
   s4u::VirtualMachine::on_shutdown(*get_iface());
+  get_iface()->on_this_shutdown(*get_iface());
 }
 
 /** @brief Change the physical host on which the given VM is running
@@ -402,12 +404,14 @@ void VirtualMachineImpl::start_migration()
 {
   is_migrating_ = true;
   s4u::VirtualMachine::on_migration_start(*get_iface());
+  get_iface()->on_this_migration_start(*get_iface());
 }
 
 void VirtualMachineImpl::end_migration()
 {
   is_migrating_ = false;
   s4u::VirtualMachine::on_migration_end(*get_iface());
+  get_iface()->on_this_migration_end(*get_iface());
 }
 
 void VirtualMachineImpl::seal()
index 0d5ed17..b0abe21 100644 (file)
@@ -3,6 +3,7 @@
 /* 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. */
 
+#include <simgrid/s4u/Comm.hpp>
 #include <simgrid/s4u/Host.hpp>
 
 #include "src/kernel/activity/CommImpl.hpp"
@@ -15,6 +16,21 @@ namespace simgrid::kernel::resource {
 /************
  * Resource *
  ************/
+static void update_bw_comm_start(const s4u::Comm& comm)
+{
+  auto* pimpl = static_cast<activity::CommImpl*>(comm.get_impl());
+
+  auto const* actionWifi = dynamic_cast<const kernel::resource::WifiLinkAction*>(pimpl->model_action_);
+  if (actionWifi == nullptr)
+    return;
+
+  if (auto* link_src = actionWifi->get_src_link()) {
+    link_src->inc_active_flux();
+  }
+  if (auto* link_dst = actionWifi->get_dst_link()) {
+    link_dst->inc_active_flux();
+  }
+}
 
 WifiLinkImpl::WifiLinkImpl(const std::string& name, const std::vector<double>& bandwidths, lmm::System* system)
     : StandardLinkImpl(name)
@@ -22,7 +38,7 @@ WifiLinkImpl::WifiLinkImpl(const std::string& name, const std::vector<double>& b
   this->set_constraint(system->constraint_new(this, 1));
   for (auto bandwidth : bandwidths)
     bandwidths_.push_back({bandwidth, 1.0, nullptr});
-  kernel::activity::CommImpl::on_start.connect(&update_bw_comm_start);
+  s4u::Comm::on_start_cb(&update_bw_comm_start);
   s4u::Link::on_communication_state_change_cb(&update_bw_comm_end);
 }
 
@@ -79,20 +95,6 @@ void WifiLinkImpl::dec_active_flux()
   nb_active_flux_--;
 }
 
-void WifiLinkImpl::update_bw_comm_start(const kernel::activity::CommImpl& comm)
-{
-  auto const* actionWifi = dynamic_cast<const simgrid::kernel::resource::WifiLinkAction*>(comm.model_action_);
-  if (actionWifi == nullptr)
-    return;
-
-  if (auto* link_src = actionWifi->get_src_link()) {
-    link_src->inc_active_flux();
-  }
-  if (auto* link_dst = actionWifi->get_dst_link()) {
-    link_dst->inc_active_flux();
-  }
-}
-
 void WifiLinkImpl::update_bw_comm_end(const simgrid::kernel::resource::NetworkAction& action,
                                       simgrid::kernel::resource::Action::State /*state*/)
 {
index cd08f6d..969eeeb 100644 (file)
@@ -53,9 +53,7 @@ public:
   void set_latency(double) override;
   bool toggle_callback();
 
-  static void update_bw_comm_start(const kernel::activity::CommImpl&);
-  static void update_bw_comm_end(const simgrid::kernel::resource::NetworkAction& action,
-                                 simgrid::kernel::resource::Action::State state);
+  static void update_bw_comm_end(const NetworkAction& action, Action::State state);
   void inc_active_flux();
   void dec_active_flux();
   static double wifi_link_dynamic_sharing(const WifiLinkImpl& link, double capacity, int n);
index 2b1e5d1..36cc2a3 100644 (file)
@@ -445,7 +445,7 @@ Action* NetworkCm02Model::communicate(s4u::Host* src, s4u::Host* dst, double siz
 
   if (cfg_weight_S_parameter > 0) {
     action->sharing_penalty_ = std::accumulate(route.begin(), route.end(), action->sharing_penalty_,
-                                               [](double total, StandardLinkImpl* const& link) {
+                                               [](double total, StandardLinkImpl const* link) {
                                                  return total + cfg_weight_S_parameter / link->get_bandwidth();
                                                });
   }
index afec012..df4abf1 100644 (file)
@@ -41,7 +41,7 @@ SIMGRID_REGISTER_NETWORK_MODEL(
       engine->get_netzone_root()->set_network_model(net_model);
 
       simgrid::s4u::Link::on_communication_state_change_cb(NetworkIBModel::IB_action_state_changed_callback);
-      simgrid::kernel::activity::CommImpl::on_start.connect(NetworkIBModel::IB_comm_start_callback);
+      simgrid::s4u::Comm::on_start_cb(NetworkIBModel::IB_comm_start_callback);
       simgrid::s4u::Host::on_creation_cb(NetworkIBModel::IB_create_host_callback);
       simgrid::config::set_default<double>("network/weight-S", 8775);
     });
@@ -68,9 +68,9 @@ void NetworkIBModel::IB_action_state_changed_callback(NetworkAction& action, Act
   ibModel->active_comms.erase(&action);
 }
 
-void NetworkIBModel::IB_comm_start_callback(const activity::CommImpl& comm)
+void NetworkIBModel::IB_comm_start_callback(const s4u::Comm& comm)
 {
-  auto* action  = static_cast<NetworkAction*>(comm.model_action_);
+  auto* action  = static_cast<NetworkAction*>(static_cast<activity::CommImpl*>(comm.get_impl())->model_action_);
   auto* ibModel = static_cast<NetworkIBModel*>(action->get_model());
   auto* act_src = &ibModel->active_nodes.at(action->get_src().get_name());
   auto* act_dst = &ibModel->active_nodes.at(action->get_dst().get_name());
index 572f18b..1400d83 100644 (file)
@@ -51,7 +51,7 @@ public:
 
   static void IB_create_host_callback(s4u::Host const& host);
   static void IB_action_state_changed_callback(NetworkAction& action, Action::State /*previous*/);
-  static void IB_comm_start_callback(const activity::CommImpl& comm);
+  static void IB_comm_start_callback(const s4u::Comm& comm);
 };
 } // namespace simgrid::kernel::resource
 #endif
index 2d3ccd9..0ba9186 100644 (file)
@@ -50,7 +50,6 @@ XBT_LOG_NEW_DEFAULT_SUBCATEGORY(res_ns3, res_network, "Network model based on ns
  *****************/
 
 extern std::map<std::string, SgFlow*, std::less<>> flow_from_sock;
-extern std::map<std::string, ns3::ApplicationContainer, std::less<>> sink_from_sock;
 
 static int number_of_links    = 1;
 static int number_of_networks = 1;
@@ -288,8 +287,9 @@ static void XBT_ATTRIB_CONSTRUCTOR(800) simgrid_ns3_network_model_register()
       });
 }
 
-static simgrid::config::Flag<std::string>
-    ns3_tcp_model("ns3/TcpModel", "The ns-3 tcp model can be: NewReno or Reno or Tahoe", "default");
+static simgrid::config::Flag<std::string> ns3_network_model_name("ns3/NetworkModel", {"ns3/TcpModel"},
+                                                                 "The ns-3 tcp model can be: NewReno or Cubic",
+                                                                 "default", [](const std::string&) {});
 static simgrid::config::Flag<std::string> ns3_seed(
     "ns3/seed",
     "The random seed provided to ns-3. Either 'time' to seed with time(), blank to not set (default), or a number.", "",
@@ -317,20 +317,34 @@ NetworkNS3Model::NetworkNS3Model(const std::string& name) : NetworkModel(name)
              "LinkEnergy plugin and ns-3 network models are not compatible. Are you looking for Ecofen, maybe?");
 
   NetPointNs3::EXTENSION_ID = routing::NetPoint::extension_create<NetPointNs3>();
+  auto const& NetworkProtocol = ns3_network_model_name.get();
+
+  if (NetworkProtocol == "UDP") {
+    /*UdpClient=0
+UdpEchoClientApplication=0
+UdpEchoServerApplication=0
+UdpL4Protocol=0
+UdpServer=0
+UdpSocket=0
+UdpSocketImpl=0
+UdpTraceClient=0*/
+    LogComponentEnable("UdpSocket", ns3::LOG_LEVEL_DEBUG);
+    LogComponentEnable("UdpL4Protocol", ns3::LOG_LEVEL_DEBUG);
+  } else {
+    ns3::Config::SetDefault("ns3::TcpSocket::SegmentSize", ns3::UintegerValue(1000));
+    ns3::Config::SetDefault("ns3::TcpSocket::DelAckCount", ns3::UintegerValue(1));
+    ns3::Config::SetDefault("ns3::TcpSocketBase::Timestamp", ns3::BooleanValue(false));
+  }
 
-  ns3::Config::SetDefault("ns3::TcpSocket::SegmentSize", ns3::UintegerValue(1000));
-  ns3::Config::SetDefault("ns3::TcpSocket::DelAckCount", ns3::UintegerValue(1));
-  ns3::Config::SetDefault("ns3::TcpSocketBase::Timestamp", ns3::BooleanValue(false));
-
-  if (auto const& TcpProtocol = ns3_tcp_model.get(); TcpProtocol == "default") {
-    /* nothing to do */
+  if (NetworkProtocol == "NewReno" || NetworkProtocol == "Cubic") {
+    XBT_INFO("Switching Tcp protocol to '%s'", NetworkProtocol.c_str());
+    ns3::Config::SetDefault("ns3::TcpL4Protocol::SocketType", ns3::StringValue("ns3::Tcp" + NetworkProtocol));
 
-  } else if (TcpProtocol == "Reno" || TcpProtocol == "NewReno" || TcpProtocol == "Tahoe") {
-    XBT_INFO("Switching Tcp protocol to '%s'", TcpProtocol.c_str());
-    ns3::Config::SetDefault("ns3::TcpL4Protocol::SocketType", ns3::StringValue("ns3::Tcp" + TcpProtocol));
+  } else if (NetworkProtocol == "UDP") {
+    XBT_INFO("Switching network protocol to UDP.");
 
-  } else {
-    xbt_die("The ns3/TcpModel must be: NewReno or Reno or Tahoe");
+  } else if (NetworkProtocol != "default") {
+    xbt_die("The ns3/NetworkModel must be: NewReno, Cubic or UDP but it's '%s'", NetworkProtocol.c_str());
   }
 
   routing::NetPoint::on_creation.connect([](routing::NetPoint& pt) {
@@ -465,7 +479,7 @@ void NetworkNS3Model::update_actions_state(double now, double delta)
 
       std::vector<StandardLinkImpl*> route;
       action->get_src().route_to(&action->get_dst(), route, nullptr);
-      for (auto const& link : route)
+      for (auto const* link : route)
         instr::resource_set_utilization("LINK", "bandwidth_used", link->get_cname(), action->get_category(),
                                         data_delta_sent / delta, now - delta, delta);
 
@@ -493,7 +507,6 @@ void NetworkNS3Model::update_actions_state(double now, double delta)
     }
     delete flow;
     flow_from_sock.erase(ns3_socket);
-    sink_from_sock.erase(ns3_socket);
   }
 }
 
@@ -571,17 +584,18 @@ NetworkNS3Action::NetworkNS3Action(Model* model, double totalBytes, s4u::Host* s
              dst->get_netpoint()->get_cname());
 
   ns3::PacketSinkHelper sink("ns3::TcpSocketFactory", ns3::InetSocketAddress(ns3::Ipv4Address::GetAny(), port_number));
-  ns3::ApplicationContainer apps = sink.Install(dst_node);
+  sink.Install(dst_node);
 
   ns3::Ptr<ns3::Socket> sock = ns3::Socket::CreateSocket(src_node, ns3::TcpSocketFactory::GetTypeId());
 
-  XBT_DEBUG("Create socket %s for a flow of %.0f Bytes from %s to %s with Interface %s",
-            transform_socket_ptr(sock).c_str(), totalBytes, src->get_cname(), dst->get_cname(), addr.c_str());
+  auto sock_addr = transform_socket_ptr(sock);
+  XBT_DEBUG("Create socket %s for a flow of %.0f Bytes from %s to %s with Interface %s", sock_addr.c_str(), totalBytes,
+            src->get_cname(), dst->get_cname(), addr.c_str());
 
-  flow_from_sock.try_emplace(transform_socket_ptr(sock), new SgFlow(static_cast<uint32_t>(totalBytes), this));
-  sink_from_sock.try_emplace(transform_socket_ptr(sock), apps);
+  flow_from_sock.try_emplace(sock_addr, new SgFlow(static_cast<uint32_t>(totalBytes), this));
 
   sock->Bind(ns3::InetSocketAddress(port_number));
+
   ns3::Simulator::ScheduleNow(&start_flow, sock, addr.c_str(), port_number);
 
   port_number = 1 + (port_number % UINT16_MAX);
@@ -612,8 +626,9 @@ void NetworkNS3Action::update_remains_lazy(double /*now*/)
 
 ns3::Ptr<ns3::Node> get_ns3node_from_sghost(const simgrid::s4u::Host* host)
 {
-  xbt_assert(host->get_netpoint()->extension<NetPointNs3>() != nullptr, "Please only use this function on ns-3 nodes");
-  return host->get_netpoint()->extension<NetPointNs3>()->ns3_node_;
+  auto* netext = host->get_netpoint()->extension<NetPointNs3>();
+  xbt_assert(netext != nullptr, "Please only use this function on ns-3 nodes");
+  return netext->ns3_node_;
 }
 } // namespace simgrid
 
index d536282..f68c1f5 100644 (file)
@@ -12,7 +12,7 @@
 #include "src/kernel/resource/NetworkModel.hpp"
 #include "src/kernel/resource/StandardLinkImpl.hpp"
 
-namespace simgrid ::kernel::resource {
+namespace simgrid::kernel::resource {
 
 class NetworkNS3Model : public NetworkModel {
 public:
index 0c13eab..bbc6206 100644 (file)
 
 #include <algorithm>
 
-std::map<std::string, SgFlow*, std::less<>> flow_from_sock;                   // ns3::sock -> SgFlow
-std::map<std::string, ns3::ApplicationContainer, std::less<>> sink_from_sock; // ns3::sock -> ns3::PacketSink
+std::map<std::string, SgFlow*, std::less<>> flow_from_sock; // ns3::sock -> SgFlow
 
 static void receive_callback(ns3::Ptr<ns3::Socket> socket);
 static void datasent_cb(ns3::Ptr<ns3::Socket> socket, uint32_t dataSent);
 
 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(res_ns3);
 
-SgFlow::SgFlow(uint32_t totalBytes, simgrid::kernel::resource::NetworkNS3Action* action)
-    : total_bytes_(totalBytes), remaining_(totalBytes), action_(action)
-{
-}
-
 static SgFlow* getFlowFromSocket(ns3::Ptr<ns3::Socket> socket)
 {
   auto it = flow_from_sock.find(transform_socket_ptr(socket));
   return (it == flow_from_sock.end()) ? nullptr : it->second;
 }
 
-static ns3::ApplicationContainer* getSinkFromSocket(ns3::Ptr<ns3::Socket> socket)
-{
-  auto it = sink_from_sock.find(transform_socket_ptr(socket));
-  return (it == sink_from_sock.end()) ? nullptr : &(it->second);
-}
-
 static void receive_callback(ns3::Ptr<ns3::Socket> socket)
 {
   SgFlow* flow = getFlowFromSocket(socket);
@@ -57,8 +45,7 @@ static void receive_callback(ns3::Ptr<ns3::Socket> socket)
 
 static void send_cb(ns3::Ptr<ns3::Socket> sock, uint32_t /*txSpace*/)
 {
-  SgFlow* flow                          = getFlowFromSocket(sock);
-  const ns3::ApplicationContainer* sink = getSinkFromSocket(sock);
+  SgFlow* flow = getFlowFromSocket(sock);
   XBT_DEBUG("Asked to write on F[%p, total: %u, remain: %u]", flow, flow->total_bytes_, flow->remaining_);
 
   if (flow->remaining_ == 0) // all data was already buffered (and socket was already closed)
@@ -85,15 +72,7 @@ static void send_cb(ns3::Ptr<ns3::Socket> sock, uint32_t /*txSpace*/)
   }
 
   if (flow->buffered_bytes_ >= flow->total_bytes_) {
-    XBT_DEBUG("Closing Sockets of flow %p", flow);
-    // Closing the sockets of the receiving application
-    ns3::Ptr<ns3::PacketSink> app        = ns3::DynamicCast<ns3::PacketSink, ns3::Application>(sink->Get(0));
-    ns3::Ptr<ns3::Socket> listening_sock = app->GetListeningSocket();
-    listening_sock->Close();
-    listening_sock->SetRecvCallback(ns3::MakeNullCallback<void, ns3::Ptr<ns3::Socket>>());
-    for (ns3::Ptr<ns3::Socket> accepted_sock : app->GetAcceptedSockets())
-      accepted_sock->Close();
-    // Closing the socket of the sender
+    XBT_DEBUG("Closing sender's socket of flow %p", flow);
     sock->Close();
   }
 }
@@ -141,10 +120,11 @@ XBT_ATTRIB_NORETURN static void failedConnect_callback(ns3::Ptr<ns3::Socket> soc
 void start_flow(ns3::Ptr<ns3::Socket> sock, const char* to, uint16_t port_number)
 {
   SgFlow* flow = getFlowFromSocket(sock);
-  ns3::InetSocketAddress serverAddr(to, port_number);
 
+  ns3::InetSocketAddress serverAddr(to, port_number);
   sock->Connect(serverAddr);
-  // tell the tcp implementation to call send_cb again
+
+  // tell the network implementation to call send_cb again
   // if we blocked and new tx buffer space becomes available
   sock->SetSendCallback(MakeCallback(&send_cb));
   // Notice when we actually sent some data (mostly for the TRACING module)
index 375b6bb..d7e35a4 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <ns3/node.h>
 #include <ns3/tcp-socket-factory.h>
+#include <ns3/udp-socket-factory.h>
 #include <ns3/wifi-module.h>
 
 #include <cstdint>
@@ -31,7 +32,10 @@ XBT_PRIVATE void ns3_add_direct_route(const simgrid::kernel::routing::NetPoint*
 
 class XBT_PRIVATE SgFlow {
 public:
-  SgFlow(uint32_t total_bytes, simgrid::kernel::resource::NetworkNS3Action* action);
+  SgFlow(uint32_t totalBytes, simgrid::kernel::resource::NetworkNS3Action* action)
+      : total_bytes_(totalBytes), remaining_(totalBytes), action_(action)
+  {
+  }
 
   // private:
   std::uint32_t buffered_bytes_ = 0;
index 769292c..eefedf3 100644 (file)
@@ -194,7 +194,7 @@ L07Action::L07Action(Model* model, const std::vector<s4u::Host*>& host_list, con
       host_list_[k / host_nb]->route_to(host_list_[k % host_nb], route, &lat);
       latency = std::max(latency, lat);
 
-      for (auto const& link : route)
+      for (auto const* link : route)
         affected_links.insert(link->get_cname());
     }
 
@@ -223,7 +223,7 @@ L07Action::L07Action(Model* model, const std::vector<s4u::Host*>& host_list, con
       std::vector<StandardLinkImpl*> route;
       host_list_[k / host_nb]->route_to(host_list_[k % host_nb], route, nullptr);
 
-      for (auto const& link : route)
+      for (auto const* link : route)
         model->get_maxmin_system()->expand(link->get_constraint(), this->get_variable(), bytes_amount[k]);
     }
   }
index d7db18f..7bac5d6 100644 (file)
@@ -3,15 +3,16 @@
 /* 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. */
 
+#ifndef HOST_L07_HPP_
+#define HOST_L07_HPP_
+
 #include "src/kernel/resource/HostImpl.hpp"
 #include "src/kernel/resource/NetworkModel.hpp"
+#include "src/simgrid/math_utils.h"
 #include <cstdlib>
 #include <vector>
 #include <xbt/base.h>
 
-#ifndef HOST_L07_HPP_
-#define HOST_L07_HPP_
-
 namespace simgrid::kernel::resource {
 
 /***********
index 9030233..5ed1d62 100644 (file)
@@ -622,8 +622,8 @@ void NetZoneImpl::get_graph(const s_xbt_graph_t* graph, std::map<std::string, xb
 {
   std::vector<NetPoint*> vertices = get_vertices();
 
-  for (auto const& my_src : vertices) {
-    for (auto const& my_dst : vertices) {
+  for (auto const* my_src : vertices) {
+    for (auto const* my_dst : vertices) {
       if (my_src == my_dst)
         continue;
 
index 11fd3b4..8019943 100644 (file)
@@ -17,7 +17,7 @@
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(ker_routing_torus, ker_platform, "Kernel Torus Routing");
 
 namespace simgrid {
-namespace kernel ::routing {
+namespace kernel::routing {
 
 void TorusZone::create_torus_links(unsigned long id, int rank, unsigned long position)
 {
index be3a2d1..64f1627 100644 (file)
@@ -13,6 +13,7 @@
 #include "src/kernel/xml/simgrid_dtd.h"
 
 #include <map>
+#include <memory>
 #include <string>
 #include <vector>
 
index 23528c0..2a2abe8 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <simgrid/Exception.hpp>
 #include <simgrid/kernel/routing/NetPoint.hpp>
+#include <simgrid/s4u/Disk.hpp>
 #include <simgrid/s4u/Engine.hpp>
 #include <simgrid/s4u/Host.hpp>
 #include <xbt/file.hpp>
@@ -259,10 +260,57 @@ void STag_simgrid_parse_platform()
                            "The most recent formalism that this version of SimGrid understands is v4.1.\n"
                            "Please update your code, or use another, more adapted, file.");
 }
+
+static void add_remote_disks()
+{
+  for (auto const& host : simgrid::s4u::Engine::get_instance()->get_all_hosts()) {
+    const char* remote_disk_str = host->get_property("remote_disk");
+    if (not remote_disk_str)
+      continue;
+    std::vector<std::string> tokens;
+    boost::split(tokens, remote_disk_str, boost::is_any_of(":"));
+    simgrid::s4u::Host* remote_host = simgrid::s4u::Host::by_name_or_null(tokens[2]);
+    xbt_assert(remote_host, "You're trying to access a host that does not exist. Please check your platform file");
+
+    const simgrid::s4u::Disk* disk = nullptr;
+    for (auto const& d : remote_host->get_disks())
+      if (d->get_name() == tokens[1]) {
+        disk = d;
+        break;
+      }
+
+    xbt_assert(disk, "You're trying to mount a disk that does not exist. Please check your platform file");
+    host->add_disk(disk);
+
+    XBT_DEBUG("Host '%s' wants to access a remote disk: %s of %s", host->get_cname(), disk->get_cname(),
+              remote_host->get_cname());
+    XBT_DEBUG("Host '%s' now has %zu disks", host->get_cname(), host->get_disks().size());
+  }
+}
+
+static void remove_remote_disks()
+{
+  XBT_DEBUG("Simulation is over, time to unregister remote disks if any");
+  for (auto const& host : simgrid::s4u::Engine::get_instance()->get_all_hosts()) {
+    const char* remote_disk_str = host->get_property("remote_disk");
+    if (remote_disk_str) {
+      std::vector<std::string> tokens;
+      boost::split(tokens, remote_disk_str, boost::is_any_of(":"));
+      XBT_DEBUG("Host '%s' wants to unmount a remote disk: %s of %s", host->get_cname(),
+                tokens[1].c_str(), tokens[2].c_str());
+      host->remove_disk(tokens[1]);
+      XBT_DEBUG("Host '%s' now has %zu disks", host->get_cname(), host->get_disks().size());
+    }
+  }
+}
+
 void ETag_simgrid_parse_platform()
 {
+  simgrid::s4u::Engine::on_platform_created_cb(&add_remote_disks);
+  simgrid::s4u::Engine::on_simulation_end_cb(&remove_remote_disks);
   if (fire_on_platform_created_callback)
     simgrid::s4u::Engine::on_platform_created();
+
 }
 
 void STag_simgrid_parse_prop()
@@ -535,7 +583,7 @@ void ETag_simgrid_parse_link()
 
 void STag_simgrid_parse_link___ctn()
 {
-  const auto engine = simgrid::s4u::Engine::get_instance();
+  const auto* engine = simgrid::s4u::Engine::get_instance();
   const simgrid::s4u::Link* link;
   simgrid::s4u::LinkInRoute::Direction direction = simgrid::s4u::LinkInRoute::Direction::NONE;
   switch (A_simgrid_parse_link___ctn_direction) {
@@ -957,7 +1005,7 @@ void simgrid_parse(bool fire_on_platform_created_callback_param)
   simgrid_parse_assert(not err, "Flex returned an error code");
 
   /* Actually connect the traces now that every elements are created */
-  const auto engine = simgrid::s4u::Engine::get_instance();
+  const auto* engine = simgrid::s4u::Engine::get_instance();
 
   for (auto const& [trace, name] : trace_connect_list_host_avail) {
     simgrid_parse_assert(traces_set_list.find(trace) != traces_set_list.end(),
index 1b2055d..0d64feb 100644 (file)
@@ -373,12 +373,12 @@ static void sg_platf_build_hostlink(simgrid::kernel::routing::StarZone* zone,
                                     const simgrid::kernel::routing::HostLinkCreationArgs* hostlink,
                                     const simgrid::s4u::Link* backbone)
 {
-  const auto engine = simgrid::s4u::Engine::get_instance();
-  auto netpoint     = engine->host_by_name(hostlink->id)->get_netpoint();
+  const auto* engine = simgrid::s4u::Engine::get_instance();
+  auto* netpoint     = engine->host_by_name(hostlink->id)->get_netpoint();
   xbt_assert(netpoint, "Host '%s' not found!", hostlink->id.c_str());
 
-  const auto linkUp   = engine->link_by_name_or_null(hostlink->link_up);
-  const auto linkDown = engine->link_by_name_or_null(hostlink->link_down);
+  const auto* linkUp   = engine->link_by_name_or_null(hostlink->link_up);
+  const auto* linkDown = engine->link_by_name_or_null(hostlink->link_down);
 
   xbt_assert(linkUp, "Link '%s' not found!", hostlink->link_up.c_str());
   xbt_assert(linkDown, "Link '%s' not found!", hostlink->link_down.c_str());
index 315ed8a..2e8e136 100644 (file)
@@ -9,6 +9,7 @@
 #include "src/kernel/activity/CommImpl.hpp"
 #include "src/mc/remote/RemotePtr.hpp"
 
+#include <algorithm>
 #include <exception>
 #include <vector>
 
@@ -97,8 +98,10 @@ public:
       mark_done();
     return times_considered_++;
   }
+  unsigned int get_max_considered() const { return max_consider_; }
   unsigned int get_times_considered() const { return times_considered_; }
   unsigned int get_times_not_considered() const { return max_consider_ - times_considered_; }
+  bool has_more_to_consider() const { return get_times_not_considered() > 0; }
   aid_t get_aid() const { return aid_; }
 
   /* returns whether the actor is marked as enabled in the application side */
@@ -115,16 +118,42 @@ public:
   }
   void mark_done() { this->state_ = InterleavingType::done; }
 
-  inline Transition* get_transition(unsigned times_considered) const
+  /**
+   * @brief Retrieves the transition that we should consider for execution by
+   * this actor from the State instance with respect to which this ActorState object
+   * is considered
+   */
+  std::shared_ptr<Transition> get_transition() const
+  {
+    // The rationale for this selection is as follows:
+    //
+    // 1. For transitions with only one possibility of execution,
+    // we always wish to select action `0` even if we've
+    // marked the transition already as considered (which
+    // we'll do if we explore a trace following that transition).
+    //
+    // 2. For transitions that can be considered multiple
+    // times, we want to be sure to select the most up-to-date
+    // action. In general, this means selecting that which is
+    // now being considered at this state. If, however, we've
+    // executed the
+    //
+    // The formula satisfies both of the above conditions:
+    //
+    // > std::clamp(times_considered_, 0u, max_consider_ - 1)
+    return get_transition(std::clamp(times_considered_, 0u, max_consider_ - 1));
+  }
+
+  std::shared_ptr<Transition> get_transition(unsigned times_considered) const
   {
     xbt_assert(times_considered < this->pending_transitions_.size(),
                "Actor %ld does not have a state available transition with `times_considered = %u`,\n"
                "yet one was asked for",
                aid_, times_considered);
-    return this->pending_transitions_[times_considered].get();
+    return this->pending_transitions_[times_considered];
   }
 
-  inline void set_transition(std::shared_ptr<Transition> t, unsigned times_considered)
+  void set_transition(std::shared_ptr<Transition> t, unsigned times_considered)
   {
     xbt_assert(times_considered < this->pending_transitions_.size(),
                "Actor %ld does not have a state available transition with `times_considered = %u`, "
diff --git a/src/mc/api/ClockVector.cpp b/src/mc/api/ClockVector.cpp
new file mode 100644 (file)
index 0000000..b411bba
--- /dev/null
@@ -0,0 +1,23 @@
+/* Copyright (c) 2015-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. */
+
+#include "src/mc/api/ClockVector.hpp"
+
+namespace simgrid::mc {
+
+ClockVector ClockVector::max(const ClockVector& cv1, const ClockVector& cv2)
+{
+  auto max_vector = ClockVector();
+
+  for (const auto& [aid, value] : cv1.contents)
+    max_vector[aid] = std::max(value, cv2.get(aid).value_or(0));
+
+  for (const auto& [aid, value] : cv2.contents)
+    max_vector[aid] = std::max(value, cv1.get(aid).value_or(0));
+
+  return max_vector;
+}
+
+} // namespace simgrid::mc
\ No newline at end of file
diff --git a/src/mc/api/ClockVector.hpp b/src/mc/api/ClockVector.hpp
new file mode 100644 (file)
index 0000000..5e6318b
--- /dev/null
@@ -0,0 +1,130 @@
+/* Copyright (c) 2016-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. */
+
+#ifndef SIMGRID_MC_CLOCK_VECTOR_HPP
+#define SIMGRID_MC_CLOCK_VECTOR_HPP
+
+#include "simgrid/forward.h"
+
+#include <cstdint>
+#include <initializer_list>
+#include <optional>
+#include <unordered_map>
+
+namespace simgrid::mc {
+
+/**
+ * @brief A multi-dimensional vector used to keep track of
+ * happens-before relation between dependent events in an
+ * execution of DPOR, SDPOR, or ODPOR
+ *
+ * Clock vectors permit actors in a distributed system
+ * to determine whether two events occurred one after the other
+ * but they may not have); i.e. they permit actors to keep track of "time".
+ * A clever observation made in the original DPOR paper is that a
+ * transition-based "happens-before" relation can be computed for
+ * any particular trace `S` using clock vectors, effectively
+ * treating dependency like the passing of a message (the context
+ * in which vector clocks are typically used).
+ *
+ * Support, however, needs to be added to clock vectors since
+ * Simgrid permits the *creation* of new actors during execution.
+ * Since we don't know this size before-hand, we have to allow
+ * clock vectors to behave as if they were "infinitely" large. To
+ * do so, all newly mapped elements, if not assigned a value, are
+ * defaulted to `0`. This corresponds to the value this actor would
+ * have had regardless had its creation been known to have evnetually
+ * occurred: no actions taken by that actor had occurred prior, so
+ * there's no way the clock vector would have been updated. In other
+ * words, when comparing clock vectors of different sizes, it's equivalent
+ * to imagine both of the same size with elements absent in one or
+ * the other implicitly mapped to zero.
+ */
+struct ClockVector final {
+private:
+  std::unordered_map<aid_t, uint32_t> contents;
+
+public:
+  ClockVector()                              = default;
+  ClockVector(const ClockVector&)            = default;
+  ClockVector& operator=(ClockVector const&) = default;
+  ClockVector(ClockVector&&)                 = default;
+  ClockVector(std::initializer_list<std::pair<const aid_t, uint32_t>> init) : contents(std::move(init)) {}
+
+  /**
+   * @brief The number of components in this
+   * clock vector
+   *
+   * A `ClockVector` implicitly maps the id of an actor
+   * it does not contain to a default value of `0`.
+   * Thus, a `ClockVector` is "lazy" in the sense
+   * that new actors are "automatically" mapped
+   * without needing to be explicitly added the clock
+   * vector when the actor is created. This means that
+   * comparison between clock vectors is possible
+   * even as actors become enabled and disabled
+   *
+   * @return uint32_t the number of elements in
+   * the clock vector
+   */
+  size_t size() const { return this->contents.size(); }
+
+  uint32_t& operator[](aid_t aid)
+  {
+    // NOTE: The `operator[]` overload of
+    // unordered_map will create a new key-value
+    // pair if `tid` does not exist and will use
+    // a _default_ value for the value (0 in this case)
+    // which is precisely what we want here
+    return this->contents[aid];
+  }
+
+  /**
+   * @brief Retrieves the value mapped to the given
+   * actor if it is contained in this clock vector
+   */
+  std::optional<uint32_t> get(aid_t aid) const
+  {
+    if (const auto iter = this->contents.find(aid); iter != this->contents.end())
+      return std::optional<uint32_t>{iter->second};
+    return std::nullopt;
+  }
+
+  /**
+   * @brief Computes a clock vector whose components
+   * are larger than the components of both of
+   * the given clock vectors
+   *
+   * The maximum of two clock vectors is definied to
+   * be the clock vector whose components are the maxmimum
+   * of the corresponding components of the arguments.
+   * Since the `ClockVector` class is "lazy", the two
+   * clock vectors given as arguments may not be of the same size.
+   * The resultant clock vector has components as follows:
+   *
+   * 1. For each actor that each clock vector maps, the
+   * resulting clock vector maps that thread to the maxmimum
+   * of the values mapped for the actor in each clock vector
+   *
+   * 2. For each actor that only a single clock vector maps,
+   * the resulting clock vector maps that thread to the
+   * value mapped by the lone clock vector
+   *
+   * The scheme is equivalent to assuming that an unmapped
+   * thread by any one clock vector is implicitly mapped to zero
+   *
+   * @param cv1 the first clock vector
+   * @param cv2  the second clock vector
+   * @return a clock vector whose components are at
+   * least as large as the corresponding components of each clock
+   * vector and whose size is large enough to contain the union
+   * of components of each clock vector
+   */
+  static ClockVector max(const ClockVector& cv1, const ClockVector& cv2);
+};
+
+} // namespace simgrid::mc
+
+#endif
index 753b136..007b179 100644 (file)
@@ -11,6 +11,7 @@
 #include "src/mc/explo/Exploration.hpp"
 #include "src/mc/mc_config.hpp"
 
+#include <algorithm>
 #include <boost/range/algorithm.hpp>
 
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_state, mc, "Logging specific to MC states");
@@ -76,16 +77,15 @@ State::State(RemoteApp& remote_app, std::shared_ptr<State> parent_state)
      * And if we kept it and the actor is enabled in this state, mark the actor as already done, so that
      * it is not explored*/
     for (auto& [aid, transition] : parent_state_->get_sleep_set()) {
-      if (not incoming_transition_->depends(&transition)) {
+      if (not incoming_transition_->depends(transition.get())) {
         sleep_set_.try_emplace(aid, transition);
         if (strategy_->actors_to_run_.count(aid) != 0) {
           XBT_DEBUG("Actor %ld will not be explored, for it is in the sleep set", aid);
-
           strategy_->actors_to_run_.at(aid).mark_done();
         }
       } else
         XBT_DEBUG("Transition >>%s<< removed from the sleep set because it was dependent with incoming >>%s<<",
-                  transition.to_string().c_str(), incoming_transition_->to_string().c_str());
+                  transition->to_string().c_str(), incoming_transition_->to_string().c_str());
     }
   }
 }
@@ -133,6 +133,11 @@ std::pair<aid_t, int> State::next_transition_guided() const
   return strategy_->next_transition();
 }
 
+aid_t State::next_odpor_transition() const
+{
+  return wakeup_tree_.get_min_single_process_actor().value_or(-1);
+}
+
 // This should be done in GuidedState, or at least interact with it
 std::shared_ptr<Transition> State::execute_next(aid_t next, RemoteApp& app)
 {
@@ -145,7 +150,7 @@ std::shared_ptr<Transition> State::execute_next(aid_t next, RemoteApp& app)
   // when simcall_handle will be called on it
   auto& actor_state                        = strategy_->actors_to_run_.at(next);
   const unsigned times_considered          = actor_state.do_consider();
-  const auto* expected_executed_transition = actor_state.get_transition(times_considered);
+  const auto* expected_executed_transition = actor_state.get_transition(times_considered).get();
   xbt_assert(expected_executed_transition != nullptr,
              "Expected a transition with %u times considered to be noted in actor %ld", times_considered, next);
 
@@ -174,4 +179,122 @@ std::shared_ptr<Transition> State::execute_next(aid_t next, RemoteApp& app)
 
   return outgoing_transition_;
 }
+
+std::unordered_set<aid_t> State::get_backtrack_set() const
+{
+  std::unordered_set<aid_t> actors;
+  for (const auto& [aid, state] : get_actors_list()) {
+    if (state.is_todo() or state.is_done()) {
+      actors.insert(aid);
+    }
+  }
+  return actors;
+}
+
+std::unordered_set<aid_t> State::get_sleeping_actors() const
+{
+  std::unordered_set<aid_t> actors;
+  for (const auto& [aid, _] : get_sleep_set()) {
+    actors.insert(aid);
+  }
+  return actors;
+}
+
+std::unordered_set<aid_t> State::get_enabled_actors() const
+{
+  std::unordered_set<aid_t> actors;
+  for (const auto& [aid, state] : get_actors_list()) {
+    if (state.is_enabled()) {
+      actors.insert(aid);
+    }
+  }
+  return actors;
+}
+
+void State::seed_wakeup_tree_if_needed(const odpor::Execution& prior)
+{
+  // TODO: It would be better not to have such a flag.
+  if (has_initialized_wakeup_tree) {
+    return;
+  }
+  // TODO: Note that the next action taken by the actor may be updated
+  // after it executes. But we will have already inserted it into the
+  // tree and decided upon "happens-before" at that point for different
+  // executions :(
+  if (wakeup_tree_.empty()) {
+    // Find an enabled transition to pick
+    for (const auto& [_, actor] : get_actors_list()) {
+      if (actor.is_enabled()) {
+        // For each variant of the transition, we want
+        // to insert the action into the tree. This ensures
+        // that all variants are searched
+        for (unsigned times = 0; times < actor.get_max_considered(); ++times) {
+          wakeup_tree_.insert(prior, odpor::PartialExecution{actor.get_transition(times)});
+        }
+        break; // Only one actor gets inserted (see pseudocode)
+      }
+    }
+  }
+  has_initialized_wakeup_tree = true;
+}
+
+void State::sprout_tree_from_parent_state()
+{
+  xbt_assert(parent_state_ != nullptr, "Attempting to construct a wakeup tree for the root state "
+                                       "(or what appears to be, rather for state without a parent defined)");
+  const auto min_process_node = parent_state_->wakeup_tree_.get_min_single_process_node();
+  xbt_assert(min_process_node.has_value(), "Attempting to construct a subtree for a substate from a "
+                                           "parent with an empty wakeup tree. This indicates either that ODPOR "
+                                           "actor selection in State.cpp is incorrect, or that the code "
+                                           "deciding when to make subtrees in ODPOR is incorrect");
+  xbt_assert((get_transition_in()->aid_ == min_process_node.value()->get_actor()) &&
+                 (get_transition_in()->type_ == min_process_node.value()->get_action()->type_),
+             "We tried to make a subtree from a parent state who claimed to have executed `%s` "
+             "but whose wakeup tree indicates it should have executed `%s`. This indicates "
+             "that exploration is not following ODPOR. Are you sure you're choosing actors "
+             "to schedule from the wakeup tree?",
+             get_transition_in()->to_string(false).c_str(),
+             min_process_node.value()->get_action()->to_string(false).c_str());
+  this->wakeup_tree_ = odpor::WakeupTree::make_subtree_rooted_at(min_process_node.value());
+}
+
+void State::remove_subtree_using_current_out_transition()
+{
+  if (auto out_transition = get_transition_out(); out_transition != nullptr) {
+    if (const auto min_process_node = wakeup_tree_.get_min_single_process_node(); min_process_node.has_value()) {
+      xbt_assert((out_transition->aid_ == min_process_node.value()->get_actor()) &&
+                     (out_transition->type_ == min_process_node.value()->get_action()->type_),
+                 "We tried to make a subtree from a parent state who claimed to have executed `%s` "
+                 "but whose wakeup tree indicates it should have executed `%s`. This indicates "
+                 "that exploration is not following ODPOR. Are you sure you're choosing actors "
+                 "to schedule from the wakeup tree?",
+                 out_transition->to_string(false).c_str(),
+                 min_process_node.value()->get_action()->to_string(false).c_str());
+    }
+  }
+  wakeup_tree_.remove_min_single_process_subtree();
+}
+
+odpor::WakeupTree::InsertionResult State::insert_into_wakeup_tree(const odpor::PartialExecution& pe,
+                                                                  const odpor::Execution& E)
+{
+  return this->wakeup_tree_.insert(E, pe);
+}
+
+void State::do_odpor_unwind()
+{
+  if (auto out_transition = get_transition_out(); out_transition != nullptr) {
+    remove_subtree_using_current_out_transition();
+
+    // Only when we've exhausted all variants of the transition which
+    // can be chosen from this state do we finally add the actor to the
+    // sleep set. This ensures that the current logic handling sleep sets
+    // works with ODPOR in the way we intend it to work. There is not a
+    // good way to perform transition equality in SimGrid; instead, we
+    // effectively simply check for the presence of an actor in the sleep set.
+    if (!get_actors_list().at(out_transition->aid_).has_more_to_consider())
+      add_sleep_set(std::move(out_transition));
+  }
+}
+
 } // namespace simgrid::mc
index e60e547..cfa440d 100644 (file)
@@ -7,8 +7,10 @@
 #define SIMGRID_MC_STATE_HPP
 
 #include "src/mc/api/ActorState.hpp"
+#include "src/mc/api/ClockVector.hpp"
 #include "src/mc/api/RemoteApp.hpp"
 #include "src/mc/api/strategy/Strategy.hpp"
+#include "src/mc/explo/odpor/WakeupTree.hpp"
 #include "src/mc/transition/Transition.hpp"
 
 #if SIMGRID_HAVE_STATEFUL_MC
@@ -42,7 +44,15 @@ class XBT_PRIVATE State : public xbt::Extendable<State> {
   /* Sleep sets are composed of the actor and the corresponding transition that made it being added to the sleep
    * set. With this information, it is check whether it should be removed from it or not when exploring a new
    * transition */
-  std::map<aid_t, Transition> sleep_set_;
+  std::map<aid_t, std::shared_ptr<Transition>> sleep_set_;
+
+  /**
+   * The wakeup tree with respect to the execution represented
+   * by the totality of all states before and including this one
+   * and with respect to this state's sleep set
+   */
+  odpor::WakeupTree wakeup_tree_;
+  bool has_initialized_wakeup_tree = false;
 
 public:
   explicit State(RemoteApp& remote_app);
@@ -50,10 +60,16 @@ public:
   /* Returns a positive number if there is another transition to pick, or -1 if not */
   aid_t next_transition() const; // this function should disapear as it is redundant with the next one
 
-  /* Same as next_transition, but choice is now guided, and an integer corresponding to the
+  /* Same as next_transition(), but choice is now guided, and an integer corresponding to the
    internal cost of the transition is returned */
   std::pair<aid_t, int> next_transition_guided() const;
 
+  /**
+   * Same as next_transition(), but the choice is not based off the ODPOR
+   * wakeup tree associated with this state
+   */
+  aid_t next_odpor_transition() const;
+
   /**
    * @brief Explore a new path on the remote app; the parameter 'next' must be the result of a previous call to
    * next_transition()
@@ -66,8 +82,8 @@ public:
 
   /* Marking as TODO some actor in this state:
    *  + consider_one mark aid actor (and assert it is possible)
-   *  + consider_best ensure one actor is marked by eventually marking the best regarding its guiding methode
-   *  + conside_all mark all enabled actor that are not done yet */
+   *  + consider_best ensure one actor is marked by eventually marking the best regarding its guiding method
+   *  + consider_all mark all enabled actor that are not done yet */
   void consider_one(aid_t aid) const { strategy_->consider_one(aid); }
   void consider_best() const { strategy_->consider_best(); }
   unsigned long consider_all() const { return strategy_->consider_all(); }
@@ -85,12 +101,76 @@ public:
   Snapshot* get_system_state() const { return system_state_.get(); }
   void set_system_state(std::shared_ptr<Snapshot> state) { system_state_ = std::move(state); }
 
-  std::map<aid_t, Transition> const& get_sleep_set() const { return sleep_set_; }
-  void add_sleep_set(std::shared_ptr<Transition> t)
+  /**
+   * @brief Computes the backtrack set for this state
+   * according to its definition in Simgrid.
+   *
+   * The backtrack set as it appears in DPOR, SDPOR, and ODPOR
+   * in SimGrid consists of those actors marked as `todo`
+   * (i.e. those that have yet to be explored) as well as those
+   * marked `done` (i.e. those that have already been explored)
+   * since the pseudocode in none of the above algorithms explicitly
+   * removes elements from the backtrack set. DPOR makes use
+   * explicitly of the `done` set, but we again note that the
+   * backtrack set still contains processes added to the done set.
+   */
+  std::unordered_set<aid_t> get_backtrack_set() const;
+  std::unordered_set<aid_t> get_sleeping_actors() const;
+  std::unordered_set<aid_t> get_enabled_actors() const;
+  std::map<aid_t, std::shared_ptr<Transition>> const& get_sleep_set() const { return sleep_set_; }
+  void add_sleep_set(std::shared_ptr<Transition> t) { sleep_set_.insert_or_assign(t->aid_, std::move(t)); }
+  bool is_actor_sleeping(aid_t actor) const
   {
-    sleep_set_.insert_or_assign(t->aid_, Transition(t->type_, t->aid_, t->times_considered_));
+    return std::find_if(sleep_set_.begin(), sleep_set_.end(), [=](const auto& pair) { return pair.first == actor; }) !=
+           sleep_set_.end();
   }
 
+  /**
+   * @brief Inserts an arbitrary enabled actor into the wakeup tree
+   * associated with this state, if such an actor exists and if
+   * the wakeup tree is already not empty
+   *
+   * @param prior The sequence of steps leading up to this state
+   * with respec to which the tree associated with this state should be
+   * a wakeup tree (wakeup trees are defined relative to an execution)
+   *
+   * @invariant: You should not manipulate a wakeup tree with respect
+   * to more than one execution; doing so will almost certainly lead to
+   * unexpected results as wakeup trees are defined relative to a single
+   * execution
+   */
+  void seed_wakeup_tree_if_needed(const odpor::Execution& prior);
+
+  /**
+   * @brief Initializes the wakeup_tree_ instance by taking the subtree rooted at the
+   * single-process node `N` running actor `p := "actor taken by parent to form this state"`
+   * of the *parent's* wakeup tree
+   */
+  void sprout_tree_from_parent_state();
+
+  /**
+   * @brief Removes the subtree rooted at the single-process node
+   * `N` running actor `p` of this state's wakeup tree
+   */
+  void remove_subtree_using_current_out_transition();
+  bool has_empty_tree() const { return this->wakeup_tree_.empty(); }
+
+  /**
+   * @brief
+   */
+  odpor::WakeupTree::InsertionResult insert_into_wakeup_tree(const odpor::PartialExecution&, const odpor::Execution&);
+
+  /** @brief Prepares the state for re-exploration following
+   * another after having followed ODPOR from this state with
+   * the current out transition
+   *
+   * After ODPOR has completed searching a maximal trace, it
+   * finds the first point in the execution with a nonempty wakeup
+   * tree. This method corresponds to lines 20 and 21 in the ODPOR
+   * pseudocode
+   */
+  void do_odpor_unwind();
+
   /* Returns the total amount of states created so far (for statistics) */
   static long get_expanded_states() { return expended_states_; }
 };
index d1ee3a8..67c1ac8 100644 (file)
@@ -6,6 +6,8 @@
 #ifndef SIMGRID_MC_BASICSTRATEGY_HPP
 #define SIMGRID_MC_BASICSTRATEGY_HPP
 
+#include "Strategy.hpp"
+
 namespace simgrid::mc {
 
 /** Basic MC guiding class which corresponds to no guide. When asked for different states
index 8b5d094..8af7774 100644 (file)
@@ -3,11 +3,15 @@
 /* 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. */
 
-#include "xbt/asserts.h"
-
 #ifndef SIMGRID_MC_STRATEGY_HPP
 #define SIMGRID_MC_STRATEGY_HPP
 
+#include "simgrid/forward.h"
+#include "src/mc/api/RemoteApp.hpp"
+#include "xbt/asserts.h"
+#include <map>
+#include <utility>
+
 namespace simgrid::mc {
 
 class Strategy {
index c36dfff..a340c2c 100644 (file)
@@ -320,14 +320,14 @@ void CommDetExtension::handle_comm_pattern(const Transition* transition)
       }
  */
 
-Exploration* create_communication_determinism_checker(const std::vector<char*>& args, bool with_dpor)
+Exploration* create_communication_determinism_checker(const std::vector<char*>& args, ReductionMode mode)
 {
   CommDetExtension::EXTENSION_ID = simgrid::mc::Exploration::extension_create<CommDetExtension>();
   StateCommDet::EXTENSION_ID     = simgrid::mc::State::extension_create<StateCommDet>();
 
   XBT_DEBUG("********* Start communication determinism verification *********");
 
-  auto base      = new DFSExplorer(args, with_dpor, true);
+  auto base      = new DFSExplorer(args, mode, true);
   auto extension = new CommDetExtension(*base);
 
   DFSExplorer::on_exploration_start([extension](RemoteApp const&) {
index 198d45a..f540f71 100644 (file)
 #include <cassert>
 #include <cstdio>
 
+#include <algorithm>
 #include <memory>
 #include <string>
+#include <unordered_set>
 #include <vector>
 
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_dfs, mc, "DFS exploration algorithm of the model-checker");
@@ -82,6 +84,7 @@ RecordTrace DFSExplorer::get_record_trace() // override
 void DFSExplorer::restore_stack(std::shared_ptr<State> state)
 {
   stack_.clear();
+  execution_seq_     = odpor::Execution();
   auto current_state = state;
   stack_.emplace_front(current_state);
   // condition corresponds to reaching initial state
@@ -90,6 +93,18 @@ void DFSExplorer::restore_stack(std::shared_ptr<State> state)
     stack_.emplace_front(current_state);
   }
   XBT_DEBUG("Replaced stack by %s", get_record_trace().to_string().c_str());
+  if (reduction_mode_ == ReductionMode::sdpor || reduction_mode_ == ReductionMode::odpor) {
+    // NOTE: The outgoing transition for the top-most
+    // state of the  stack refers to that which was taken
+    // as part of the last trace explored by the algorithm.
+    // Thus, only the sequence of transitions leading up to,
+    // but not including, the last state must be included
+    // when reconstructing the Exploration for SDPOR.
+    for (auto iter = std::next(stack_.begin()); iter != stack_.end(); ++iter) {
+      execution_seq_.push_transition((*iter)->get_transition_in());
+    }
+    XBT_DEBUG("Replaced SDPOR/ODPOR execution to reflect the new stack");
+  }
 }
 
 void DFSExplorer::log_state() // override
@@ -125,8 +140,14 @@ void DFSExplorer::run()
         XBT_ERROR("/!\\ Max depth of %d reached! THIS WILL PROBABLY BREAK the dpor reduction /!\\",
                   _sg_mc_max_depth.get());
         XBT_ERROR("/!\\ If bad things happen, disable dpor with --cfg=model-check/reduction:none /!\\");
-      } else
+      } else if (reduction_mode_ == ReductionMode::sdpor || reduction_mode_ == ReductionMode::odpor) {
+        XBT_ERROR("/!\\ Max depth of %d reached! THIS **WILL** BREAK the reduction, which is not sound "
+                  "when stopping at a fixed depth /!\\",
+                  _sg_mc_max_depth.get());
+        XBT_ERROR("/!\\ If bad things happen, disable the reduction with --cfg=model-check/reduction:none /!\\");
+      } else {
         XBT_WARN("/!\\ Max depth reached ! /!\\ ");
+      }
       this->backtrack();
       continue;
     }
@@ -143,9 +164,25 @@ void DFSExplorer::run()
     }
 #endif
 
+    if (reduction_mode_ == ReductionMode::odpor) {
+      // In the case of ODPOR, the wakeup tree for this
+      // state may be empty if we're exploring new territory
+      // (rather than following the partial execution of a
+      // wakeup tree). This corresponds to lines 9 to 13 of
+      // the ODPOR pseudocode
+      //
+      // INVARIANT: The execution sequence should be consistent
+      // with the state when seeding the tree. If the sequence
+      // gets out of sync with the state, selection will not
+      // work as we intend
+      state->seed_wakeup_tree_if_needed(execution_seq_);
+    }
+
     // Search for the next transition
-    // next_transition returns a pair<aid_t, int> in case we want to consider multiple state (eg. during backtrack)
-    auto [next, _] = state->next_transition_guided();
+    // next_transition returns a pair<aid_t, int>
+    // in case we want to consider multiple states (eg. during backtrack)
+    const aid_t next = reduction_mode_ == ReductionMode::odpor ? state->next_odpor_transition()
+                                                               : std::get<0>(state->next_transition_guided());
 
     if (next < 0) { // If there is no more transition in the current state, backtrack.
       XBT_VERB("%lu actors remain, but none of them need to be interleaved (depth %zu).", state->get_actor_count(),
@@ -164,11 +201,11 @@ void DFSExplorer::run()
     if (_sg_mc_sleep_set && XBT_LOG_ISENABLED(mc_dfs, xbt_log_priority_verbose)) {
       XBT_VERB("Sleep set actually containing:");
       for (auto& [aid, transition] : state->get_sleep_set())
-        XBT_VERB("  <%ld,%s>", aid, transition.to_string().c_str());
+        XBT_VERB("  <%ld,%s>", aid, transition->to_string().c_str());
     }
 
     /* Actually answer the request: let's execute the selected request (MCed does one step) */
-    state->execute_next(next, get_remote_app());
+    const auto executed_transition = state->execute_next(next, get_remote_app());
     on_transition_execute_signal(state->get_transition_out().get(), get_remote_app());
 
     // If there are processes to interleave and the maximum depth has not been
@@ -180,13 +217,25 @@ void DFSExplorer::run()
     auto next_state = std::make_shared<State>(get_remote_app(), state);
     on_state_creation_signal(next_state.get(), get_remote_app());
 
-    /* Sleep set procedure:
-     * adding the taken transition to the sleep set of the original state.
-     * <!> Since the parent sleep set is used to compute the child sleep set, this need to be
-     * done after next_state creation */
-    XBT_DEBUG("Marking Transition >>%s<< of process %ld done and adding it to the sleep set",
-              state->get_transition_out()->to_string().c_str(), state->get_transition_out()->aid_);
-    state->add_sleep_set(state->get_transition_out()); // Actors are marked done when they are considerd in ActorState
+    if (reduction_mode_ == ReductionMode::odpor) {
+      // With ODPOR, after taking a step forward, we must
+      // assign a copy of that subtree to the next state.
+      //
+      // NOTE: We only add actions to the sleep set AFTER
+      // we've regenerated states. We must perform the search
+      // fully down a single path before we consider adding
+      // any elements to the sleep set according to the pseudocode
+      next_state->sprout_tree_from_parent_state();
+    } else {
+      /* Sleep set procedure:
+       * adding the taken transition to the sleep set of the original state.
+       * <!> Since the parent sleep set is used to compute the child sleep set, this need to be
+       * done after next_state creation */
+      XBT_DEBUG("Marking Transition >>%s<< of process %ld done and adding it to the sleep set",
+                state->get_transition_out()->to_string().c_str(), state->get_transition_out()->aid_);
+      state->add_sleep_set(
+          state->get_transition_out()); // Actors are marked done when they are considered in ActorState
+    }
 
     /* DPOR persistent set procedure:
      * for each new transition considered, check if it depends on any other previous transition executed before it
@@ -229,6 +278,45 @@ void DFSExplorer::run()
         }
         tmp_stack.pop_back();
       }
+    } else if (reduction_mode_ == ReductionMode::sdpor) {
+      /**
+       * SDPOR Source Set Procedure:
+       *
+       * Find "reversible races" in the current execution `E` with respect
+       * to the latest action `p`. For each such race, determine one thread
+       * not contained in the backtrack set at the "race point" `r` which
+       * "represents" the trace formed by first executing everything after
+       * `r` that doesn't depend on it (`v := notdep(r, E)`) and then `p` to
+       * flip the race.
+       *
+       * The intuition is that some subsequence of `v` may enable `p`, so
+       * we want to be sure that search "in that direction"
+       */
+      execution_seq_.push_transition(std::move(executed_transition));
+      xbt_assert(execution_seq_.get_latest_event_handle().has_value(), "No events are contained in the SDPOR execution "
+                                                                       "even though one was just added");
+
+      const auto next_E_p = execution_seq_.get_latest_event_handle().value();
+      for (const auto e_race : execution_seq_.get_reversible_races_of(next_E_p)) {
+        State* prev_state  = stack_[e_race].get();
+        const auto choices = execution_seq_.get_missing_source_set_actors_from(e_race, prev_state->get_backtrack_set());
+        if (!choices.empty()) {
+          // NOTE: To incorporate the idea of attempting to select the "best"
+          // backtrack point into SDPOR, instead of selecting the `first` initial,
+          // we should instead compute all choices and decide which is best
+          //
+          // Here, we choose the actor with the lowest ID to ensure
+          // we get deterministic results
+          const auto q =
+              std::min_element(choices.begin(), choices.end(), [](const aid_t a1, const aid_t a2) { return a1 < a2; });
+          prev_state->consider_one(*q);
+          opened_states_.emplace_back(std::move(prev_state));
+        }
+      }
+    } else if (reduction_mode_ == ReductionMode::odpor) {
+      // In the case of ODPOR, we simply observe the transition that was executed
+      // until we've reached a maximal trace
+      execution_seq_.push_transition(std::move(executed_transition));
     }
 
     // Before leaving that state, if the transition we just took can be taken multiple times, we
@@ -240,7 +328,8 @@ void DFSExplorer::run()
       this->check_non_termination(next_state.get());
 
 #if SIMGRID_HAVE_STATEFUL_MC
-    /* Check whether we already explored next_state in the past (but only if interested in state-equality reduction) */
+    /* Check whether we already explored next_state in the past (but only if interested in state-equality reduction)
+     */
     if (_sg_mc_max_visited_states > 0)
       visited_state_ = visited_states_.addVisitedState(next_state->get_num(), next_state.get(), get_remote_app());
 #endif
@@ -304,8 +393,75 @@ std::shared_ptr<State> DFSExplorer::best_opened_state()
   return best_state;
 }
 
+std::shared_ptr<State> DFSExplorer::next_odpor_state()
+{
+  for (auto iter = stack_.rbegin(); iter != stack_.rend(); ++iter) {
+    const auto& state = *iter;
+    state->do_odpor_unwind();
+    XBT_DEBUG("\tPerformed ODPOR 'clean-up'. Sleep set has:");
+    for (auto& [aid, transition] : state->get_sleep_set())
+      XBT_DEBUG("\t  <%ld,%s>", aid, transition->to_string().c_str());
+    if (!state->has_empty_tree()) {
+      return state;
+    }
+  }
+  return nullptr;
+}
+
 void DFSExplorer::backtrack()
 {
+  if (const auto last_event = execution_seq_.get_latest_event_handle();
+      reduction_mode_ == ReductionMode::odpor and last_event.has_value()) {
+    /**
+     * ODPOR Race Detection Procedure:
+     *
+     * For each reversible race in the current execution, we
+     * note if there are any continuations `C` equivalent to that which
+     * would reverse the race that have already either a) been searched by ODPOR or
+     * b) been *noted* to be searched by the wakeup tree at the
+     * appropriate reversal point, either as `C` directly or
+     * an as equivalent to `C` ("eventually looks like C", viz. the `~_E`
+     * relation)
+     */
+    for (auto e_prime = static_cast<odpor::Execution::EventHandle>(0); e_prime <= last_event.value(); ++e_prime) {
+      for (const auto e : execution_seq_.get_reversible_races_of(e_prime)) {
+        XBT_DEBUG("ODPOR: Reversible race detected between events `%u` and `%u`", e, e_prime);
+        State& prev_state = *stack_[e];
+        if (const auto v = execution_seq_.get_odpor_extension_from(e, e_prime, prev_state); v.has_value()) {
+          const auto result = prev_state.insert_into_wakeup_tree(v.value(), execution_seq_.get_prefix_before(e));
+          switch (result) {
+            case odpor::WakeupTree::InsertionResult::root: {
+              XBT_DEBUG("ODPOR: Reversible race with `%u` unaccounted for in the wakeup tree for "
+                        "the execution prior to event `%u`:",
+                        e_prime, e);
+              break;
+            }
+            case odpor::WakeupTree::InsertionResult::interior_node: {
+              XBT_DEBUG("ODPOR: Reversible race with `%u` partially accounted for in the wakeup tree for "
+                        "the execution prior to event `%u`:",
+                        e_prime, e);
+              break;
+            }
+            case odpor::WakeupTree::InsertionResult::leaf: {
+              XBT_DEBUG("ODPOR: Reversible race with `%u` accounted for in the wakeup tree for "
+                        "the execution prior to event `%u`:",
+                        e_prime, e);
+              break;
+            }
+          }
+          for (const auto& seq : simgrid::mc::odpor::get_textual_trace(v.value())) {
+            XBT_DEBUG(" %s", seq.c_str());
+          }
+        } else {
+          XBT_DEBUG("ODPOR: Ignoring race: `sleep(E')` intersects `WI_[E'](v := notdep(%u, E))`", e);
+          XBT_DEBUG("Sleep set contains:");
+          for (auto& [aid, transition] : prev_state.get_sleep_set())
+            XBT_DEBUG("  <%ld,%s>", aid, transition->to_string().c_str());
+        }
+      }
+    }
+  }
+
   XBT_VERB("Backtracking from %s", get_record_trace().to_string().c_str());
   XBT_DEBUG("%lu alternatives are yet to be explored:", opened_states_.size());
 
@@ -313,7 +469,7 @@ void DFSExplorer::backtrack()
   get_remote_app().check_deadlock();
 
   // Take the point with smallest distance
-  auto backtracking_point = best_opened_state();
+  auto backtracking_point = reduction_mode_ == ReductionMode::odpor ? next_odpor_state() : best_opened_state();
 
   // if no backtracking point, then set the stack_ to empty so we can end the exploration
   if (not backtracking_point) {
@@ -372,20 +528,16 @@ void DFSExplorer::backtrack()
   this->restore_stack(backtracking_point);
 }
 
-DFSExplorer::DFSExplorer(const std::vector<char*>& args, bool with_dpor, bool need_memory_info)
+DFSExplorer::DFSExplorer(const std::vector<char*>& args, ReductionMode mode, bool need_memory_info)
     : Exploration(args, need_memory_info || _sg_mc_termination
 #if SIMGRID_HAVE_STATEFUL_MC
                             || _sg_mc_checkpoint > 0
 #endif
-      )
+                  )
+    , reduction_mode_(mode)
 {
-  if (with_dpor)
-    reduction_mode_ = ReductionMode::dpor;
-  else
-    reduction_mode_ = ReductionMode::none;
-
   if (_sg_mc_termination) {
-    if (with_dpor) {
+    if (mode != ReductionMode::none) {
       XBT_INFO("Check non progressive cycles (turning DPOR off)");
       reduction_mode_ = ReductionMode::none;
     } else {
@@ -409,11 +561,19 @@ DFSExplorer::DFSExplorer(const std::vector<char*>& args, bool with_dpor, bool ne
   }
   if (stack_.back()->count_todo_multiples() > 1)
     opened_states_.emplace_back(stack_.back());
+
+  if (mode == ReductionMode::odpor && !_sg_mc_sleep_set) {
+    // ODPOR requires the use of sleep sets; SDPOR
+    // "likes" using sleep sets but it is not strictly
+    // required
+    XBT_INFO("Forcing the use of sleep sets for use with ODPOR");
+    _sg_mc_sleep_set = true;
+  }
 }
 
-Exploration* create_dfs_exploration(const std::vector<char*>& args, bool with_dpor)
+Exploration* create_dfs_exploration(const std::vector<char*>& args, ReductionMode mode)
 {
-  return new DFSExplorer(args, with_dpor);
+  return new DFSExplorer(args, mode);
 }
 
 } // namespace simgrid::mc
index 27194a8..a0dc478 100644 (file)
@@ -6,26 +6,30 @@
 #ifndef SIMGRID_MC_SAFETY_CHECKER_HPP
 #define SIMGRID_MC_SAFETY_CHECKER_HPP
 
+#include "src/mc/api/ClockVector.hpp"
 #include "src/mc/api/State.hpp"
 #include "src/mc/explo/Exploration.hpp"
+#include "src/mc/explo/odpor/Execution.hpp"
+#include "src/mc/mc_config.hpp"
 
 #if SIMGRID_HAVE_STATEFUL_MC
 #include "src/mc/VisitedState.hpp"
 #endif
 
+#include <deque>
 #include <list>
 #include <memory>
 #include <set>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 namespace simgrid::mc {
 
-using stack_t = std::list<std::shared_ptr<State>>;
+using stack_t = std::deque<std::shared_ptr<State>>;
 
 class XBT_PRIVATE DFSExplorer : public Exploration {
-  XBT_DECLARE_ENUM_CLASS(ReductionMode, none, dpor);
-
+private:
   ReductionMode reduction_mode_;
   unsigned long backtrack_count_      = 0; // for statistics
   unsigned long visited_states_count_ = 0; // for statistics
@@ -43,7 +47,7 @@ class XBT_PRIVATE DFSExplorer : public Exploration {
   static xbt::signal<void(RemoteApp&)> on_log_state_signal;
 
 public:
-  explicit DFSExplorer(const std::vector<char*>& args, bool with_dpor, bool need_memory_info = false);
+  explicit DFSExplorer(const std::vector<char*>& args, ReductionMode mode, bool need_memory_info = false);
   void run() override;
   RecordTrace get_record_trace() override;
   void log_state() override;
@@ -93,6 +97,16 @@ private:
 
   /** Stack representing the position in the exploration graph */
   stack_t stack_;
+
+  /**
+   * Provides additional metadata about the position in the exploration graph
+   * which is used by SDPOR and ODPOR
+   */
+  odpor::Execution execution_seq_;
+
+  /** Per-actor clock vectors used to compute the "happens-before" relation */
+  std::unordered_map<aid_t, ClockVector> per_actor_clocks_;
+
 #if SIMGRID_HAVE_STATEFUL_MC
   VisitedStates visited_states_;
   std::unique_ptr<VisitedState> visited_state_;
@@ -105,6 +119,11 @@ private:
   std::vector<std::shared_ptr<State>> opened_states_;
   std::shared_ptr<State> best_opened_state();
 
+  /** If we're running ODPOR, picks the corresponding state in the stack
+   * (opened_states_ are ignored)
+   */
+  std::shared_ptr<State> next_odpor_state();
+
   /** Change current stack_ value to correspond to the one we would have
    *  had if we executed transition to get to state. This is required when
    *  backtracking, and achieved thanks to the fact states save their parent.*/
index 824adcd..64114d4 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "simgrid/forward.h"
 #include "src/mc/api/RemoteApp.hpp"
+#include "src/mc/mc_config.hpp"
 #include "src/mc/mc_exit.hpp"
 #include "src/mc/mc_record.hpp"
 #include <xbt/Extendable.hpp>
@@ -40,7 +41,7 @@ public:
 
   static Exploration* get_instance() { return instance_; }
   // No copy:
-  Exploration(Exploration const&) = delete;
+  Exploration(Exploration const&)            = delete;
   Exploration& operator=(Exploration const&) = delete;
 
   /** Main function of this algorithm */
@@ -72,8 +73,8 @@ public:
 
 // External constructors so that the types (and the types of their content) remain hidden
 XBT_PUBLIC Exploration* create_liveness_checker(const std::vector<char*>& args);
-XBT_PUBLIC Exploration* create_dfs_exploration(const std::vector<char*>& args, bool with_dpor);
-XBT_PUBLIC Exploration* create_communication_determinism_checker(const std::vector<char*>& args, bool with_dpor);
+XBT_PUBLIC Exploration* create_dfs_exploration(const std::vector<char*>& args, ReductionMode mode);
+XBT_PUBLIC Exploration* create_communication_determinism_checker(const std::vector<char*>& args, ReductionMode mode);
 XBT_PUBLIC Exploration* create_udpor_checker(const std::vector<char*>& args);
 
 } // namespace simgrid::mc
index 0e23ab0..02f2a8e 100644 (file)
@@ -7,6 +7,7 @@
 #ifndef SIMGRID_MC_UDPOR_CHECKER_HPP
 #define SIMGRID_MC_UDPOR_CHECKER_HPP
 
+#include "src/mc/api/State.hpp"
 #include "src/mc/explo/Exploration.hpp"
 #include "src/mc/explo/udpor/Configuration.hpp"
 #include "src/mc/explo/udpor/EventSet.hpp"
diff --git a/src/mc/explo/odpor/ClockVector_test.cpp b/src/mc/explo/odpor/ClockVector_test.cpp
new file mode 100644 (file)
index 0000000..9d49c60
--- /dev/null
@@ -0,0 +1,366 @@
+/* Copyright (c) 2017-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. */
+
+#include "src/3rd-party/catch.hpp"
+#include "src/mc/api/ClockVector.hpp"
+
+using namespace simgrid::mc;
+
+TEST_CASE("simgrid::mc::ClockVector: Constructing Vectors")
+{
+  SECTION("Without values")
+  {
+    ClockVector cv;
+    REQUIRE(cv.size() == 0);
+
+    // Verify `cv` doesn't map any values
+    REQUIRE_FALSE(cv.get(0).has_value());
+    REQUIRE_FALSE(cv.get(1).has_value());
+    REQUIRE_FALSE(cv.get(2).has_value());
+    REQUIRE_FALSE(cv.get(3).has_value());
+  }
+
+  SECTION("With initial values")
+  {
+    ClockVector cv{
+        {1, 5}, {3, 1}, {7, 10}, {6, 5}, {8, 1}, {10, 10},
+    };
+    REQUIRE(cv.size() == 6);
+
+    // Verify `cv` maps each value
+    REQUIRE(cv.get(1).has_value());
+    REQUIRE(cv.get(1).value() == 5);
+    REQUIRE(cv[1] == 5);
+    REQUIRE(cv.get(3).has_value());
+    REQUIRE(cv.get(3).value() == 1);
+    REQUIRE(cv[3] == 1);
+    REQUIRE(cv.get(7).has_value());
+    REQUIRE(cv.get(7).value() == 10);
+    REQUIRE(cv[7] == 10);
+    REQUIRE(cv.get(6).has_value());
+    REQUIRE(cv.get(6).value() == 5);
+    REQUIRE(cv[6] == 5);
+    REQUIRE(cv.get(8).has_value());
+    REQUIRE(cv.get(8).value() == 1);
+    REQUIRE(cv[8] == 1);
+    REQUIRE(cv.get(10).has_value());
+    REQUIRE(cv.get(10).value() == 10);
+    REQUIRE(cv[10] == 10);
+  }
+}
+
+TEST_CASE("simgrid::mc::ClockVector: Testing operator[]")
+{
+  ClockVector cv;
+  cv[0] = 1;
+  REQUIRE(cv.size() == 1);
+
+  REQUIRE(cv.get(0).has_value());
+  REQUIRE(cv.get(0).value() == 1);
+
+  // Verify `cv` doesn't map other values
+  REQUIRE_FALSE(cv.get(2).has_value());
+  REQUIRE_FALSE(cv.get(3).has_value());
+
+  cv[10] = 31;
+  REQUIRE(cv.size() == 2);
+
+  // Old values are still mapped
+  REQUIRE(cv.get(0).has_value());
+  REQUIRE(cv.get(0).value() == 1);
+  REQUIRE(cv[0] == 1);
+  REQUIRE(cv.get(10).has_value());
+  REQUIRE(cv.get(10).value() == 31);
+  REQUIRE(cv[10] == 31);
+
+  // Verify `cv` doesn't map other values
+  REQUIRE_FALSE(cv.get(2).has_value());
+  REQUIRE_FALSE(cv.get(3).has_value());
+}
+
+TEST_CASE("simgrid::mc::ClockVector: Testing Maximal Clock Vectors")
+{
+  SECTION("Max with zero clock vector yields self")
+  {
+    ClockVector cv1{
+        {1, 2},
+        {2, 10},
+        {3, 5},
+    };
+    ClockVector cv2;
+    ClockVector maxCV = ClockVector::max(cv1, cv2);
+
+    REQUIRE(maxCV.size() == 3);
+    REQUIRE(maxCV.get(1).has_value());
+    REQUIRE(maxCV.get(1).value() == 2);
+    REQUIRE(maxCV[1] == 2);
+
+    REQUIRE(maxCV.get(2).has_value());
+    REQUIRE(maxCV.get(2).value() == 10);
+    REQUIRE(maxCV[2] == 10);
+
+    REQUIRE(maxCV.get(3).has_value());
+    REQUIRE(maxCV.get(3).value() == 5);
+    REQUIRE(maxCV[3] == 5);
+  }
+
+  SECTION("Max with self clock vector yields self")
+  {
+    ClockVector cv1{
+        {1, 2},
+        {2, 10},
+        {3, 5},
+    };
+    ClockVector maxCV = ClockVector::max(cv1, cv1);
+
+    REQUIRE(maxCV.size() == 3);
+    REQUIRE(maxCV.get(1).has_value());
+    REQUIRE(maxCV.get(1).value() == 2);
+    REQUIRE(maxCV[1] == 2);
+
+    REQUIRE(maxCV.get(2).has_value());
+    REQUIRE(maxCV.get(2).value() == 10);
+    REQUIRE(maxCV[2] == 10);
+
+    REQUIRE(maxCV.get(3).has_value());
+    REQUIRE(maxCV.get(3).value() == 5);
+    REQUIRE(maxCV[3] == 5);
+  }
+
+  SECTION("Testing with partial overlaps")
+  {
+    SECTION("Example 1")
+    {
+      ClockVector cv1{
+          {1, 2},
+          {2, 10},
+          {3, 5},
+      };
+      ClockVector cv2{
+          {1, 5},
+          {3, 1},
+          {7, 10},
+      };
+      ClockVector maxCV = ClockVector::max(cv1, cv2);
+
+      REQUIRE(maxCV.size() == 4);
+      REQUIRE(maxCV.get(1).has_value());
+      REQUIRE(maxCV.get(1).value() == 5);
+      REQUIRE(maxCV[1] == 5);
+
+      REQUIRE(maxCV.get(2).has_value());
+      REQUIRE(maxCV.get(2).value() == 10);
+      REQUIRE(maxCV[2] == 10);
+
+      REQUIRE(maxCV.get(3).has_value());
+      REQUIRE(maxCV.get(3).value() == 5);
+      REQUIRE(maxCV[3] == 5);
+
+      REQUIRE(maxCV.get(7).has_value());
+      REQUIRE(maxCV.get(7).value() == 10);
+      REQUIRE(maxCV[7] == 10);
+    }
+
+    SECTION("Example 2")
+    {
+      ClockVector cv1{
+          {1, 2}, {2, 10}, {3, 5}, {4, 40}, {11, 3}, {12, 8},
+      };
+      ClockVector cv2{
+          {1, 18}, {2, 4}, {4, 41}, {10, 3}, {12, 8},
+      };
+      ClockVector maxCV = ClockVector::max(cv1, cv2);
+
+      REQUIRE(maxCV.size() == 7);
+      REQUIRE(maxCV.get(1).has_value());
+      REQUIRE(maxCV.get(1).value() == 18);
+      REQUIRE(maxCV[1] == 18);
+
+      REQUIRE(maxCV.get(2).has_value());
+      REQUIRE(maxCV.get(2).value() == 10);
+      REQUIRE(maxCV[2] == 10);
+
+      REQUIRE(maxCV.get(3).has_value());
+      REQUIRE(maxCV.get(3).value() == 5);
+      REQUIRE(maxCV[3] == 5);
+
+      REQUIRE(maxCV.get(4).has_value());
+      REQUIRE(maxCV.get(4).value() == 41);
+      REQUIRE(maxCV[4] == 41);
+
+      REQUIRE(maxCV.get(10).has_value());
+      REQUIRE(maxCV.get(10).value() == 3);
+      REQUIRE(maxCV[10] == 3);
+
+      REQUIRE(maxCV.get(11).has_value());
+      REQUIRE(maxCV.get(11).value() == 3);
+      REQUIRE(maxCV[11] == 3);
+
+      REQUIRE(maxCV.get(12).has_value());
+      REQUIRE(maxCV.get(12).value() == 8);
+      REQUIRE(maxCV[12] == 8);
+    }
+
+    SECTION("Example 3")
+    {
+      ClockVector cv1{{1, 2}, {4, 41}, {12, 0}, {100, 5}};
+      ClockVector cv2{{2, 4}, {4, 10}, {10, 3}, {12, 8}, {19, 0}, {21, 6}, {22, 0}};
+      ClockVector cv3{{21, 60}, {22, 6}, {100, 3}};
+      ClockVector maxCV = ClockVector::max(cv1, cv2);
+      maxCV             = ClockVector::max(maxCV, cv3);
+
+      REQUIRE(maxCV.size() == 9);
+      REQUIRE(maxCV.get(1).has_value());
+      REQUIRE(maxCV.get(1).value() == 2);
+      REQUIRE(maxCV[1] == 2);
+
+      REQUIRE(maxCV.get(2).has_value());
+      REQUIRE(maxCV.get(2).value() == 4);
+      REQUIRE(maxCV[2] == 4);
+
+      REQUIRE(maxCV.get(4).has_value());
+      REQUIRE(maxCV.get(4).value() == 41);
+      REQUIRE(maxCV[4] == 41);
+
+      REQUIRE(maxCV.get(10).has_value());
+      REQUIRE(maxCV.get(10).value() == 3);
+      REQUIRE(maxCV[10] == 3);
+
+      REQUIRE(maxCV.get(12).has_value());
+      REQUIRE(maxCV.get(12).value() == 8);
+      REQUIRE(maxCV[12] == 8);
+
+      REQUIRE(maxCV.get(19).has_value());
+      REQUIRE(maxCV.get(19).value() == 0);
+      REQUIRE(maxCV[19] == 0);
+
+      REQUIRE(maxCV.get(21).has_value());
+      REQUIRE(maxCV.get(21).value() == 60);
+      REQUIRE(maxCV[21] == 60);
+
+      REQUIRE(maxCV.get(22).has_value());
+      REQUIRE(maxCV.get(22).value() == 6);
+      REQUIRE(maxCV[22] == 6);
+
+      REQUIRE(maxCV.get(100).has_value());
+      REQUIRE(maxCV.get(100).value() == 5);
+      REQUIRE(maxCV[100] == 5);
+    }
+  }
+
+  SECTION("Testing without overlaps")
+  {
+    SECTION("Example 1")
+    {
+      ClockVector cv1{{1, 2}};
+      ClockVector cv2{
+          {2, 4},
+          {4, 41},
+          {10, 3},
+          {12, 8},
+      };
+      ClockVector maxCV = ClockVector::max(cv1, cv2);
+
+      REQUIRE(maxCV.size() == 5);
+      REQUIRE(maxCV.get(1).has_value());
+      REQUIRE(maxCV.get(1).value() == 2);
+      REQUIRE(maxCV[1] == 2);
+
+      REQUIRE(maxCV.get(2).has_value());
+      REQUIRE(maxCV.get(2).value() == 4);
+      REQUIRE(maxCV[2] == 4);
+
+      REQUIRE(maxCV.get(4).has_value());
+      REQUIRE(maxCV.get(4).value() == 41);
+      REQUIRE(maxCV[4] == 41);
+
+      REQUIRE(maxCV.get(10).has_value());
+      REQUIRE(maxCV.get(10).value() == 3);
+      REQUIRE(maxCV[10] == 3);
+
+      REQUIRE(maxCV.get(12).has_value());
+      REQUIRE(maxCV.get(12).value() == 8);
+      REQUIRE(maxCV[12] == 8);
+    }
+
+    SECTION("Example 2")
+    {
+      ClockVector cv1{{1, 2}, {4, 41}};
+      ClockVector cv2{
+          {2, 4},
+          {10, 3},
+          {12, 8},
+      };
+      ClockVector maxCV = ClockVector::max(cv1, cv2);
+
+      REQUIRE(maxCV.size() == 5);
+      REQUIRE(maxCV.get(1).has_value());
+      REQUIRE(maxCV.get(1).value() == 2);
+      REQUIRE(maxCV[1] == 2);
+
+      REQUIRE(maxCV.get(2).has_value());
+      REQUIRE(maxCV.get(2).value() == 4);
+      REQUIRE(maxCV[2] == 4);
+
+      REQUIRE(maxCV.get(4).has_value());
+      REQUIRE(maxCV.get(4).value() == 41);
+      REQUIRE(maxCV[4] == 41);
+
+      REQUIRE(maxCV.get(10).has_value());
+      REQUIRE(maxCV.get(10).value() == 3);
+      REQUIRE(maxCV[10] == 3);
+
+      REQUIRE(maxCV.get(12).has_value());
+      REQUIRE(maxCV.get(12).value() == 8);
+      REQUIRE(maxCV[12] == 8);
+    }
+
+    SECTION("Example 3")
+    {
+      ClockVector cv1{{1, 2}, {4, 41}};
+      ClockVector cv2{{2, 4}, {10, 3}, {12, 8}, {19, 0}, {21, 6}};
+      ClockVector cv3{{22, 6}, {100, 3}};
+      ClockVector maxCV = ClockVector::max(cv1, cv2);
+      maxCV             = ClockVector::max(maxCV, cv3);
+
+      REQUIRE(maxCV.size() == 9);
+      REQUIRE(maxCV.get(1).has_value());
+      REQUIRE(maxCV.get(1).value() == 2);
+      REQUIRE(maxCV[1] == 2);
+
+      REQUIRE(maxCV.get(2).has_value());
+      REQUIRE(maxCV.get(2).value() == 4);
+      REQUIRE(maxCV[2] == 4);
+
+      REQUIRE(maxCV.get(4).has_value());
+      REQUIRE(maxCV.get(4).value() == 41);
+      REQUIRE(maxCV[4] == 41);
+
+      REQUIRE(maxCV.get(10).has_value());
+      REQUIRE(maxCV.get(10).value() == 3);
+      REQUIRE(maxCV[10] == 3);
+
+      REQUIRE(maxCV.get(12).has_value());
+      REQUIRE(maxCV.get(12).value() == 8);
+      REQUIRE(maxCV[12] == 8);
+
+      REQUIRE(maxCV.get(19).has_value());
+      REQUIRE(maxCV.get(19).value() == 0);
+      REQUIRE(maxCV[19] == 0);
+
+      REQUIRE(maxCV.get(21).has_value());
+      REQUIRE(maxCV.get(21).value() == 6);
+      REQUIRE(maxCV[21] == 6);
+
+      REQUIRE(maxCV.get(22).has_value());
+      REQUIRE(maxCV.get(22).value() == 6);
+      REQUIRE(maxCV[22] == 6);
+
+      REQUIRE(maxCV.get(100).has_value());
+      REQUIRE(maxCV.get(100).value() == 3);
+      REQUIRE(maxCV[100] == 3);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/mc/explo/odpor/Execution.cpp b/src/mc/explo/odpor/Execution.cpp
new file mode 100644 (file)
index 0000000..ef10e4f
--- /dev/null
@@ -0,0 +1,490 @@
+/* Copyright (c) 2008-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. */
+
+#include "src/mc/explo/odpor/Execution.hpp"
+#include "src/mc/api/State.hpp"
+#include "src/mc/explo/odpor/ReversibleRaceCalculator.hpp"
+#include "xbt/asserts.h"
+#include "xbt/string.hpp"
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+namespace simgrid::mc::odpor {
+
+std::vector<std::string> get_textual_trace(const PartialExecution& w)
+{
+  std::vector<std::string> trace;
+  for (const auto& t : w) {
+    const auto a = xbt::string_printf("Actor %ld: %s", t->aid_, t->to_string(true).c_str());
+    trace.push_back(std::move(a));
+  }
+  return trace;
+}
+
+Execution::Execution(const PartialExecution& w)
+{
+  push_partial_execution(w);
+}
+
+void Execution::push_transition(std::shared_ptr<Transition> t)
+{
+  if (t == nullptr) {
+    throw std::invalid_argument("Unexpectedly received `nullptr`");
+  }
+  ClockVector max_clock_vector;
+  for (const Event& e : this->contents_) {
+    if (e.get_transition()->depends(t.get())) {
+      max_clock_vector = ClockVector::max(max_clock_vector, e.get_clock_vector());
+    }
+  }
+  max_clock_vector[t->aid_] = this->size();
+  contents_.push_back(Event({std::move(t), max_clock_vector}));
+}
+
+void Execution::push_partial_execution(const PartialExecution& w)
+{
+  for (const auto& t : w) {
+    push_transition(t);
+  }
+}
+
+std::vector<std::string> Execution::get_textual_trace() const
+{
+  std::vector<std::string> trace;
+  for (const auto& t : this->contents_) {
+    const auto a =
+        xbt::string_printf("Actor %ld: %s", t.get_transition()->aid_, t.get_transition()->to_string(true).c_str());
+    trace.push_back(std::move(a));
+  }
+  return trace;
+}
+
+std::unordered_set<Execution::EventHandle> Execution::get_racing_events_of(Execution::EventHandle target) const
+{
+  std::unordered_set<Execution::EventHandle> racing_events;
+  std::unordered_set<Execution::EventHandle> disqualified_events;
+
+  // For each event of the execution
+  for (auto e_i = target; e_i != std::numeric_limits<Execution::EventHandle>::max(); e_i--) {
+    // We need `e_i -->_E target` as a necessary condition
+    if (not happens_before(e_i, target)) {
+      continue;
+    }
+
+    // Further, `proc(e_i) != proc(target)`
+    if (get_actor_with_handle(e_i) == get_actor_with_handle(target)) {
+      disqualified_events.insert(e_i);
+      continue;
+    }
+
+    // There could an event that "happens-between" the two events which would discount `e_i` as a race
+    for (auto e_j = e_i; e_j < target; e_j++) {
+      // If both:
+      // 1. e_i --->_E e_j; and
+      // 2. disqualified_events.count(e_j) > 0
+      // then e_i --->_E target indirectly (either through
+      // e_j directly, or transitively through e_j)
+      if (disqualified_events.count(e_j) > 0 and happens_before(e_i, e_j)) {
+        disqualified_events.insert(e_i);
+        break;
+      }
+    }
+
+    // If `e_i` wasn't disqualified in the last round,
+    // it's in a race with `target`. After marking it
+    // as such, we ensure no other event `e` can happen-before
+    // it (since this would transitively make it the event
+    // which "happens-between" `target` and `e`)
+    if (disqualified_events.count(e_i) == 0) {
+      racing_events.insert(e_i);
+      disqualified_events.insert(e_i);
+    }
+  }
+
+  return racing_events;
+}
+
+std::unordered_set<Execution::EventHandle> Execution::get_reversible_races_of(EventHandle handle) const
+{
+  std::unordered_set<EventHandle> reversible_races;
+  for (EventHandle race : get_racing_events_of(handle)) {
+    if (ReversibleRaceCalculator::is_race_reversible(*this, race, handle)) {
+      reversible_races.insert(race);
+    }
+  }
+  return reversible_races;
+}
+
+Execution Execution::get_prefix_before(Execution::EventHandle handle) const
+{
+  return Execution(std::vector<Event>{contents_.begin(), contents_.begin() + handle});
+}
+
+std::unordered_set<aid_t>
+Execution::get_missing_source_set_actors_from(EventHandle e, const std::unordered_set<aid_t>& backtrack_set) const
+{
+  // If this execution is empty, there are no initials
+  // relative to the last transition added to the execution
+  // since such a transition does not exist
+  if (empty()) {
+    return std::unordered_set<aid_t>{};
+  }
+
+  // To actually compute `I_[E'](v) ∩ backtrack(E')`, we must
+  // first compute `E'` and "move" in the direction of `v`.
+  // We perform a scan over `E` (this execution) and make
+  // note of any events which occur after `e` but don't
+  // "happen-after" `e` by pushing them onto `E'`. Note that
+  // correctness is still preserved in computing `v` "on-the-fly"
+  // to determine if an event `e` by actor `q` is an initial for `E'`
+  // after `v`: only those events that "occur-before" `e` in `v` could
+  // "happen-before" `ve for any valid "happens-before" relation
+  // (see property 1 in the ODPOR paper, viz. "is included in <_E")
+
+  // First, grab `E' := pre(e, E)` and determine what actor `p` is
+  const auto next_E_p = get_latest_event_handle().value();
+  xbt_assert(e != next_E_p,
+             "This method assumes that the event `e` (%u) and `next_[E](p)` (%u)"
+             "are in a reversible race, yet we claim to have such a race between the"
+             "same event. This indicates the the SDPOR pseudocode implementation is broken "
+             "as it supplies these values.",
+             e, next_E_p);
+  Execution E_prime_v = get_prefix_before(e);
+  std::vector<sdpor::Execution::EventHandle> v;
+  std::unordered_set<aid_t> I_E_prime_v;
+  std::unordered_set<aid_t> disqualified_actors;
+
+  // Note `e + 1` here: `notdep(e, E)` is defined as the
+  // set of events that *occur-after* but don't *happen-after* `e`
+  for (auto e_prime = e + 1; e_prime <= next_E_p; ++e_prime) {
+    // Any event `e*` which occurs after `e` but which does not
+    // happen after `e` is a member of `v`. In addition to marking
+    // the event in `v`, we also "simulate" running the action `v`
+    // from E'
+    if (not happens_before(e, e_prime) or e_prime == next_E_p) {
+      // First, push the transition onto the hypothetical execution
+      E_prime_v.push_transition(get_event_with_handle(e_prime).get_transition());
+      const EventHandle e_prime_in_E_prime_v = E_prime_v.get_latest_event_handle().value();
+
+      // When checking whether any event in `dom_[E'](v)` happens before
+      // `next_[E'](q)` below for thread `q`, we must consider that the
+      // events relative to `E` (this execution) are different than those
+      // relative to `E'.v`. Thus e.g. event `7` in `E` may be event `4`
+      // in `E'.v`. Since we are asking about "happens-before"
+      // `-->_[E'.v]` about `E'.v`, we must build `v` relative to `E'`.
+      //
+      // Note that we add `q` to v regardless of whether `q` itself has been
+      // disqualified since  we've determined that `e_prime` "occurs-after" but
+      // does not "happen-after" `e`
+      v.push_back(e_prime_in_E_prime_v);
+
+      const aid_t q = E_prime_v.get_actor_with_handle(e_prime_in_E_prime_v);
+      if (disqualified_actors.count(q) > 0) { // Did we already note that `q` is not an initial?
+        continue;
+      }
+      const bool is_initial = std::none_of(v.begin(), v.end(), [&](const auto& e_star) {
+        return E_prime_v.happens_before(e_star, e_prime_in_E_prime_v);
+      });
+      if (is_initial) {
+        // If the backtrack set already contains `q`, we're done:
+        // they've made note to search for (or have already searched for)
+        // this initial
+        if (backtrack_set.count(q) > 0) {
+          return std::unordered_set<aid_t>{};
+        } else {
+          I_E_prime_v.insert(q);
+        }
+      } else {
+        // If `q` is disqualified as a candidate, clearly
+        // no event occurring after `e_prime` in `E` executed
+        // by actor `q` will qualify since any (valid) happens-before
+        // relation orders actions taken by each actor
+        disqualified_actors.insert(q);
+      }
+    }
+  }
+  xbt_assert(!I_E_prime_v.empty(),
+             "For any non-empty execution, we know that "
+             "at minimum one actor is an initial since "
+             "some execution is possible with respect to a "
+             "prefix before event `%u`, yet we didn't find anyone. "
+             "This implies the implementation of this function is broken.",
+             e);
+  return I_E_prime_v;
+}
+
+std::optional<PartialExecution> Execution::get_odpor_extension_from(EventHandle e, EventHandle e_prime,
+                                                                    const State& state_at_e) const
+{
+  // `e` is assumed to be in a reversible race with `e_prime`.
+  // If `e > e_prime`, then `e` occurs-after `e_prime` which means
+  // `e` could not race with if
+  if (e > e_prime) {
+    throw std::invalid_argument("ODPOR extensions can only be computed for "
+                                "events in a reversible race, which is claimed, "
+                                "yet the racing event 'occurs-after' the target");
+  }
+
+  if (empty()) {
+    return std::nullopt;
+  }
+
+  PartialExecution v;
+  std::vector<Execution::EventHandle> v_handles;
+  std::unordered_set<aid_t> WI_E_prime_v;
+  std::unordered_set<aid_t> disqualified_actors;
+  Execution E_prime_v                           = get_prefix_before(e);
+  const std::unordered_set<aid_t> sleep_E_prime = state_at_e.get_sleeping_actors();
+
+  // Note `e + 1` here: `notdep(e, E)` is defined as the
+  // set of events that *occur-after* but don't *happen-after* `e`
+  //
+  // SUBTLE NOTE: ODPOR requires us to compute `notdep(e, E)` EVEN THOUGH
+  // the race is between `e` and `e'`; that is, events occurring in `E`
+  // that "occur-after" `e'` may end up in the partial execution `v`.
+  //
+  // Observe that `notdep(e, E).proc(e')` will contain all transitions
+  // that don't happen-after `e` in the order they appear FOLLOWED BY
+  // THE **TRANSITION** ASSOCIATED WITH **`e'`**!!
+  //
+  // SUBTLE NOTE: Observe that any event that "happens-after" `e'`
+  // must necessarily "happen-after" `e` as well, since `e` and
+  // `e'` are presumed to be in a reversible race. Hence, we know that
+  // all events `e_star` such that `e` "happens-before" `e_star` cannot affect
+  // the enabledness of `e'`; furthermore, `e'` cannot affect the enabledness
+  // of any event independent with `e` that "occurs-after" `e'`
+  for (auto e_star = e + 1; e_star <= get_latest_event_handle().value(); ++e_star) {
+    // Any event `e*` which occurs after `e` but which does not
+    // happen after `e` is a member of `v`. In addition to marking
+    // the event in `v`, we also "simulate" running the action `v` from E'
+    // to be able to compute `--->[E'.v]`
+    if (not happens_before(e, e_star)) {
+      xbt_assert(e_star != e_prime,
+                 "Invariant Violation: We claimed events %u and %u were in a reversible race, yet we also "
+                 "claim that they do not happen-before one another. This is impossible: "
+                 "are you sure that the two events are in a reversible race?",
+                 e, e_prime);
+      E_prime_v.push_transition(get_event_with_handle(e_star).get_transition());
+      v.push_back(get_event_with_handle(e_star).get_transition());
+
+      const EventHandle e_star_in_E_prime_v = E_prime_v.get_latest_event_handle().value();
+
+      // When checking whether any event in `dom_[E'](v)` happens before
+      // `next_[E'](q)` below for thread `q`, we must consider that the
+      // events relative to `E` (this execution) are different than those
+      // relative to `E'.v`. Thus e.g. event `7` in `E` may be event `4`
+      // in `E'.v`. Since we are asking about "happens-before"
+      // `-->_[E'.v]` about `E'.v`, we must build `v` relative to `E'`
+      v_handles.push_back(e_star_in_E_prime_v);
+
+      // Note that we add `q` to v regardless of whether `q` itself has been
+      // disqualified since `q` may itself disqualify other actors
+      // (i.e. even if `q` is disqualified from being an initial, it
+      // is still contained in the sequence `v`)
+      const aid_t q = E_prime_v.get_actor_with_handle(e_star_in_E_prime_v);
+      if (disqualified_actors.count(q) > 0) { // Did we already note that `q` is not an initial?
+        continue;
+      }
+      const bool is_initial = std::none_of(v_handles.begin(), v_handles.end(), [&](const auto& e_star) {
+        return E_prime_v.happens_before(e_star, e_star_in_E_prime_v);
+      });
+      if (is_initial) {
+        // If the sleep set already contains `q`, we're done:
+        // we've found an initial contained in the sleep set and
+        // so the intersection is non-empty
+        if (sleep_E_prime.count(q) > 0) {
+          return std::nullopt;
+        } else {
+          WI_E_prime_v.insert(q);
+        }
+      } else {
+        // If `q` is disqualified as a candidate, clearly
+        // no event occurring after `e_prime` in `E` executed
+        // by actor `q` will qualify since any (valid) happens-before
+        // relation orders actions taken by each actor
+        disqualified_actors.insert(q);
+      }
+    }
+  }
+
+  // Now we add `e_prime := <q, i>` to `E'.v` and repeat the same work
+  // It's possible `proc(e_prime)` is an initial
+  //
+  // Note the form of `v` in the pseudocode:
+  //  `v := notdep(e, E).e'^
+  {
+    E_prime_v.push_transition(get_event_with_handle(e_prime).get_transition());
+    v.push_back(get_event_with_handle(e_prime).get_transition());
+
+    const EventHandle e_prime_in_E_prime_v = E_prime_v.get_latest_event_handle().value();
+    v_handles.push_back(e_prime_in_E_prime_v);
+
+    const bool is_initial = std::none_of(v_handles.begin(), v_handles.end(), [&](const auto& e_star) {
+      return E_prime_v.happens_before(e_star, e_prime_in_E_prime_v);
+    });
+    if (is_initial) {
+      if (const aid_t q = E_prime_v.get_actor_with_handle(e_prime_in_E_prime_v); sleep_E_prime.count(q) > 0) {
+        return std::nullopt;
+      } else {
+        WI_E_prime_v.insert(q);
+      }
+    }
+  }
+  {
+    const Execution pre_E_e    = get_prefix_before(e);
+    const auto sleeping_actors = state_at_e.get_sleeping_actors();
+
+    // Check if any enabled actor that is independent with
+    // this execution after `v` is contained in the sleep set
+    for (const auto& [aid, astate] : state_at_e.get_actors_list()) {
+      const bool is_in_WI_E =
+          astate.is_enabled() and pre_E_e.is_independent_with_execution_of(v, astate.get_transition());
+      const bool is_in_sleep_set = sleeping_actors.count(aid) > 0;
+
+      // `action(aid)` is in `WI_[E](v)` but also is contained in the sleep set.
+      // This implies that the intersection between the two is non-empty
+      if (is_in_WI_E && is_in_sleep_set) {
+        return std::nullopt;
+      }
+    }
+  }
+  return v;
+}
+
+bool Execution::is_initial_after_execution_of(const PartialExecution& w, aid_t p) const
+{
+  auto E_w = *this;
+  std::vector<EventHandle> w_handles;
+  for (const auto& w_i : w) {
+    // Take one step in the direction of `w`
+    E_w.push_transition(w_i);
+
+    // If that step happened to be executed by `p`,
+    // great: we know that `p` is contained in `w`.
+    // We now need to verify that it doens't "happen-after"
+    // any events which occur before it
+    if (w_i->aid_ == p) {
+      const auto p_handle = E_w.get_latest_event_handle().value();
+      return std::none_of(w_handles.begin(), w_handles.end(),
+                          [&](const auto handle) { return E_w.happens_before(handle, p_handle); });
+    } else {
+      w_handles.push_back(E_w.get_latest_event_handle().value());
+    }
+  }
+  return false;
+}
+
+bool Execution::is_independent_with_execution_of(const PartialExecution& w, std::shared_ptr<Transition> next_E_p) const
+{
+  // INVARIANT: Here, we assume that for any process `p_i` of `w`,
+  // the events of this execution followed by the execution of all
+  // actors occurring before `p_i` in `v` (`p_j`, `0 <= j < i`)
+  // are sufficient to enable `p_i`. This is fortunately the case
+  // with what ODPOR requires of us, viz. to ask the question about
+  // `v := notdep(e, E)` for some execution `E` and event `e` of
+  // that execution.
+  auto E_p_w = *this;
+  E_p_w.push_transition(std::move(next_E_p));
+  const auto p_handle = E_p_w.get_latest_event_handle().value();
+
+  // As we add events to `w`, verify that none
+  // of them "happen-after" the event associated with
+  // the step `next_E_p` (viz. p_handle)
+  for (const auto& w_i : w) {
+    E_p_w.push_transition(w_i);
+    const auto w_i_handle = E_p_w.get_latest_event_handle().value();
+    if (E_p_w.happens_before(p_handle, w_i_handle)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+std::optional<PartialExecution> Execution::get_shortest_odpor_sq_subset_insertion(const PartialExecution& v,
+                                                                                  const PartialExecution& w) const
+{
+  // See section 4 of Abdulla. et al.'s 2017 ODPOR paper for details (specifically
+  // where the [iterative] computation of `v ~_[E] w` is described)
+  auto E_v   = *this;
+  auto w_now = w;
+
+  for (const auto& next_E_p : v) {
+    const aid_t p = next_E_p->aid_;
+
+    // Is `p in `I_[E](w)`?
+    if (E_v.is_initial_after_execution_of(w_now, p)) {
+      // Remove `p` from w and continue
+
+      // INVARIANT: If `p` occurs in `w`, it had better refer to the same
+      // transition referenced by `v`. Unfortunately, we have two
+      // sources of truth here which can be manipulated at the same
+      // time as arguments to the function. If ODPOR works correctly,
+      // they should always refer to the same value; but as a sanity check,
+      // we have an assert that tests that at least the types are the same.
+      const auto action_by_p_in_w =
+          std::find_if(w_now.begin(), w_now.end(), [=](const auto& action) { return action->aid_ == p; });
+      xbt_assert(action_by_p_in_w != w_now.end(), "Invariant violated: actor `p` "
+                                                  "is claimed to be an initial after `w` but is "
+                                                  "not actually contained in `w`. This indicates that there "
+                                                  "is a bug computing initials");
+      const auto& w_action = *action_by_p_in_w;
+      xbt_assert(w_action->type_ == next_E_p->type_,
+                 "Invariant violated: `v` claims that actor `%ld` executes '%s' while "
+                 "`w` claims that it executes '%s'. These two partial executions both "
+                 "refer to `next_[E](p)`, which should be the same",
+                 p, next_E_p->to_string(false).c_str(), w_action->to_string(false).c_str());
+      w_now.erase(action_by_p_in_w);
+    }
+    // Is `E ⊢ p ◇ w`?
+    else if (E_v.is_independent_with_execution_of(w_now, next_E_p)) {
+      // INVARIANT: Note that it is impossible for `p` to be
+      // excluded from the set `I_[E](w)` BUT ALSO be contained in
+      // `w` itself if `E ⊢ p ◇ w` (intuitively, the fact that `E ⊢ p ◇ w`
+      // means that are able to move `p` anywhere in `w` IF it occurred, so
+      // if it really does occur we know it must then be an initial).
+      // We assert this is the case here
+      const auto action_by_p_in_w =
+          std::find_if(w_now.begin(), w_now.end(), [=](const auto& action) { return action->aid_ == p; });
+      xbt_assert(action_by_p_in_w == w_now.end(),
+                 "Invariant violated: We claimed that actor `%ld` is not an initial "
+                 "after `w`, yet it's independent with all actions of `w` AND occurs in `w`."
+                 "This indicates that there is a bug computing initials",
+                 p);
+    } else {
+      // Neither of the two above conditions hold, so the relation fails
+      return std::nullopt;
+    }
+
+    // Move one step forward in the direction of `v` and repeat
+    E_v.push_transition(next_E_p);
+  }
+  return std::optional<PartialExecution>{std::move(w_now)};
+}
+
+bool Execution::happens_before(Execution::EventHandle e1_handle, Execution::EventHandle e2_handle) const
+{
+  // 1. "happens-before" (-->_E) is a subset of "occurs before" (<_E)
+  // and is an irreflexive relation
+  if (e1_handle >= e2_handle) {
+    return false;
+  }
+
+  // Each execution maintains a stack of clock vectors which are updated
+  // according to the procedure outlined in section 4 of the original DPOR paper
+  const Event& e2     = get_event_with_handle(e2_handle);
+  const aid_t proc_e1 = get_actor_with_handle(e1_handle);
+
+  if (const auto e1_in_e2_clock = e2.get_clock_vector().get(proc_e1); e1_in_e2_clock.has_value()) {
+    return e1_handle <= e1_in_e2_clock.value();
+  }
+  // If `e1` does not appear in e2's clock vector, this implies
+  // not only that the transitions associated with `e1` and `e2
+  // are independent, but further that there are no transitive
+  // dependencies between e1 and e2
+  return false;
+}
+
+} // namespace simgrid::mc::odpor
\ No newline at end of file
diff --git a/src/mc/explo/odpor/Execution.hpp b/src/mc/explo/odpor/Execution.hpp
new file mode 100644 (file)
index 0000000..f3bd7ed
--- /dev/null
@@ -0,0 +1,346 @@
+/* Copyright (c) 2007-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. */
+
+#ifndef SIMGRID_MC_ODPOR_EXECUTION_HPP
+#define SIMGRID_MC_ODPOR_EXECUTION_HPP
+
+#include "src/mc/api/ClockVector.hpp"
+#include "src/mc/explo/odpor/odpor_forward.hpp"
+#include "src/mc/mc_forward.hpp"
+#include "src/mc/mc_record.hpp"
+#include "src/mc/transition/Transition.hpp"
+
+#include <list>
+#include <optional>
+#include <unordered_set>
+#include <vector>
+
+namespace simgrid::mc::odpor {
+
+std::vector<std::string> get_textual_trace(const PartialExecution& w);
+
+/**
+ * @brief The occurrence of a transition in an execution
+ *
+ * An execution is set of *events*, where each element represents
+ * the occurrence or execution of the `i`th step of a particular
+ * actor `j`
+ */
+class Event {
+  std::pair<std::shared_ptr<Transition>, ClockVector> contents_;
+
+public:
+  Event()                        = default;
+  Event(Event&&)                 = default;
+  Event(const Event&)            = default;
+  Event& operator=(const Event&) = default;
+  explicit Event(std::pair<std::shared_ptr<Transition>, ClockVector> pair) : contents_(std::move(pair)) {}
+
+  std::shared_ptr<Transition> get_transition() const { return std::get<0>(contents_); }
+  const ClockVector& get_clock_vector() const { return std::get<1>(contents_); }
+};
+
+/**
+ * @brief An ordered sequence of transitions which describe
+ * the evolution of a process undergoing model checking
+ *
+ * An execution conceptually is just a string of actors
+ * ids (e.g. "1.2.3.1.2.2.1.1"), where the `i`th occurrence
+ * of actor id `j` corresponds to the `i`th action executed
+ * by the actor with id `j` (viz. the `i`th step of actor `j`).
+ * Executions can stand alone on their own or can extend
+ * the execution of other sequences
+ *
+ * Executions are conceived based on the following papers:
+ * 1. "Source Sets: A Foundation for Optimal Dynamic Partial Order Reduction"
+ * by Abdulla et al.
+ *
+ * In addition to representing an actual steps taken,
+ * an execution keeps track of the "happens-before"
+ * relation among the transitions in the execution
+ * by following the procedure outlined in section 4 of the
+ * original DPOR paper with clock vectors.
+ * As new transitions are added to the execution, clock vectors are
+ * computed as appropriate and associated with the corresponding position
+ * in the execution. This allows us to determine “happens-before” in
+ * constant-time between points in the execution (called events
+ * [which is unfortunately the same name used in UDPOR for a slightly
+ * different concept]), albeit for an up-front cost of traversing the
+ * execution stack. The happens-before relation is important in many
+ * places in SDPOR and ODPOR.
+ *
+ * @note: For more nuanced happens-before relations, clock
+ * vectors may not always suffice. Clock vectors work
+ * well with transition-based dependencies like that used in
+ * SimGrid; but to have a more refined independence relation,
+ * an event-based dependency approach is needed. See the section 2
+ * in the ODPOR paper [1] concerning event-based dependencies and
+ * how the happens-before relation can be refined in a
+ * computation model much like that of SimGrid. In fact, the same issue
+ * arrises with UDPOR with context-sensitive dependencies:
+ * the two concepts are analogous if not identical
+ */
+class Execution {
+private:
+  std::vector<Event> contents_;
+  Execution(std::vector<Event>&& contents) : contents_(std::move(contents)) {}
+
+public:
+  using EventHandle = uint32_t;
+
+  Execution()                            = default;
+  Execution(const Execution&)            = default;
+  Execution& operator=(Execution const&) = default;
+  Execution(Execution&&)                 = default;
+  Execution(const PartialExecution&);
+
+  std::vector<std::string> get_textual_trace() const;
+
+  size_t size() const { return this->contents_.size(); }
+  bool empty() const { return this->contents_.empty(); }
+  auto begin() const { return this->contents_.begin(); }
+  auto end() const { return this->contents_.end(); }
+
+  /**
+   * @brief Computes the "core" portion the SDPOR algorithm,
+   * viz. the intersection of the backtracking set and the
+   * set of initials with respect to the *last* event added
+   * to the execution
+   *
+   * The "core" portion of the SDPOR algorithm is found on
+   * lines 6-9 of the pseudocode:
+   *
+   * 6 | let E' := pre(E, e)
+   * 7 | let v :=  notdep(e, E).p
+   * 8 | if I_[E'](v) ∩ backtrack(E') = empty then
+   * 9 |    --> add some q in I_[E'](v) to backtrack(E')
+   *
+   * This method computes all of the lines simultaneously,
+   * returning some actor `q` if it passes line 8 and exists.
+   * The event `e` and the set `backtrack(E')` are the provided
+   * arguments to the method.
+   *
+   * @param e the event with respect to which to determine
+   * whether a backtrack point needs to be added for the
+   * prefix corresponding to the execution prior to `e`
+   *
+   * @param backtrack_set The set of actors which should
+   * not be considered for selection as an SDPOR initial.
+   * While this set need not necessarily correspond to the
+   * backtrack set `backtrack(E')`, doing so provides what
+   * is expected for SDPOR
+   *
+   * See the SDPOR algorithm pseudocode in [1] for more
+   * details for the context of the function.
+   *
+   * @invariant: This method assumes that events `e` and
+   * `e' := get_latest_event_handle()` are in a *reversible* race
+   * as is explicitly the case in SDPOR
+   *
+   * @returns an actor not contained in `disqualified` which
+   * can serve as an initial to reverse the race between `e`
+   * and `e'`
+   */
+  std::unordered_set<aid_t> get_missing_source_set_actors_from(EventHandle e,
+                                                               const std::unordered_set<aid_t>& backtrack_set) const;
+
+  /**
+   * @brief Computes the analogous lines from the SDPOR algorithm
+   * in the ODPOR algorithm, viz. the intersection of the slee set
+   * and the set of weak initials with respect to the given pair
+   * of racing events
+   *
+   * This method computes lines 4-6 of the ODPOR pseudocode, viz.:
+   *
+   * 4 | let E' := pre(E, e)
+   * 5 | let v := notdep(e, E).e'^
+   * 6 | if sleep(E') ∩ WI_[E'](v) = empty then ...
+   *
+   * The sequence `v` is computed and returned as needed, based on whether
+   * the check on line 6 passes.
+   *
+   * @invariant: This method assumes that events `e` and
+   * `e_prime` are in a *reversible* race as is the case
+   * in ODPOR
+   */
+  std::optional<PartialExecution> get_odpor_extension_from(EventHandle e, EventHandle e_prime,
+                                                           const State& state_at_e) const;
+
+  /**
+   * @brief For a given sequence of actors `v` and a sequence of transitions `w`,
+   * computes the sequence, if any, that should be inserted as a child in wakeup tree for
+   * this execution
+   *
+   * Recall that the procedure for implementing the insertion
+   * is outlined in section 6.2 of Abdulla et al. 2017 as follows:
+   *
+   * | Let `v` be the smallest (w.r.t to "<") sequence in [the tree] B
+   * | such that `v ~_[E] w`. If `v` is a leaf node, the tree can be left
+   * | unmodified.
+   * |
+   * | Otherwise let `w'` be the shortest sequence such that `w [=_[E] v.w'`
+   * | and add `v.w'` as a new leaf, ordered after all already existing nodes
+   * | of the form `v.w''`
+   *
+   * This method computes the result `v.w'` as needed (viz. only if `v ~_[E] w`
+   * with respect to this execution `E`)
+   *
+   * The procedure for determining `v ~_[E] w` is given as Lemma 4.6 of
+   * Abdulla et al. 2017:
+   *
+   * | The relation `v ~_[E] w` holds if either
+   * | (1) v = <>, or
+   * | (2) v := p.v' and either
+   * |     (a) p in I_[E](w) and `v' ~_[E.p] (w \ p)`
+   * |     (b) E ⊢ p ◊ w and `v' ~_[E.p] w`
+   *
+   * @invariant: This method assumes that `E.v` is a valid execution, viz.
+   * that the events of `E` are sufficient to enabled `v_0` and that
+   * `v_0, ..., v_{i - 1}` are sufficient to enable `v_i`. This is the
+   * case when e.g. `v := notdep(e, E).p` for example in ODPOR
+   *
+   * @returns a partial execution `w'` that should be inserted
+   * as a child of a wakeup tree node with the associated sequence `v`.
+   */
+  std::optional<PartialExecution> get_shortest_odpor_sq_subset_insertion(const PartialExecution& v,
+                                                                         const PartialExecution& w) const;
+
+  /**
+   * @brief For a given sequence `w`, determines whether p in I_[E](w)
+   *
+   * @note: You may notice that some of the other methods compute this
+   * value as well. What we notice, though, in those cases is that
+   * we are repeatedly asking about initials with respect to an execution.
+   * It is better, then, to bunch the work together in those cases to
+   * get asymptotically better results (e.g. instead of calling with all
+   * `N` actors, we can process them "in-parallel" as is done with the
+   * computation of SDPOR initials)
+   */
+  bool is_initial_after_execution_of(const PartialExecution& w, aid_t p) const;
+
+  /**
+   * @brief Determines whether `E ⊢ p ◊ w` given the next action taken by `p`
+   */
+  bool is_independent_with_execution_of(const PartialExecution& w, std::shared_ptr<Transition> next_E_p) const;
+
+  /**
+   * @brief Determines the event associated with
+   * the given handle `handle`
+   */
+  const Event& get_event_with_handle(EventHandle handle) const { return contents_[handle]; }
+
+  /**
+   * @brief Determines the actor associated with
+   * the given event handle `handle`
+   */
+  aid_t get_actor_with_handle(EventHandle handle) const { return get_event_with_handle(handle).get_transition()->aid_; }
+
+  /**
+   * @brief Determines the transition associated with the given handle `handle`
+   */
+  const Transition* get_transition_for_handle(EventHandle handle) const
+  {
+    return get_event_with_handle(handle).get_transition().get();
+  }
+
+  /**
+   * @brief Returns a handle to the newest event of the execution,
+   * if such an event exists
+   */
+  std::optional<EventHandle> get_latest_event_handle() const
+  {
+    return contents_.empty() ? std::nullopt : std::optional<EventHandle>{static_cast<EventHandle>(size() - 1)};
+  }
+
+  /**
+   * @brief Returns a set of events which are in
+   * "immediate conflict" (according to the definition given
+   * in the ODPOR paper) with the given event
+   *
+   * Two events `e` and `e'` in an execution `E` are said to
+   * race iff
+   *
+   * 1. `proc(e) != proc(e')`; that is, the events correspond to
+   * the execution of different actors
+   * 2. `e -->_E e'` and there is no `e''` in `E` such that
+   *  `e -->_E e''` and `e'' -->_E e'`; that is, the two events
+   * "happen-before" one another in `E` and no other event in
+   * `E` "happens-between" `e` and `e'`
+   *
+   * @param handle the event with respect to which races are
+   * computed
+   * @returns a set of event handles from which race with `handle`
+   */
+  std::unordered_set<EventHandle> get_racing_events_of(EventHandle handle) const;
+
+  /**
+   * @brief Returns a set of events which are in a reversible
+   * race with the given event handle `handle`
+   *
+   * Two events `e` and `e'` in an execution `E` are said to
+   * be in a reversible race iff
+   *
+   * 1. `e` and `e'` race
+   * 2. In any equivalent execution sequence `E'` to `E`
+   * where `e` occurs immediately before `e'`, the actor
+   * running `e'` was enabled in the state prior to `e`
+   *
+   * @param handle the event with respect to which
+   * reversible races are computed
+   * @returns a set of event handles from which are in a reversible
+   * race with `handle`
+   */
+  std::unordered_set<EventHandle> get_reversible_races_of(EventHandle handle) const;
+
+  /**
+   * @brief Computes `pre(e, E)` as described in ODPOR [1]
+   *
+   * The execution `pre(e, E)` for an event `e` in an
+   * execution `E` is the contiguous prefix of events
+   * `E' <= E` up to by excluding the event `e` itself.
+   * The prefix intuitively represents the "history" of
+   * causes that permitted event `e` to exist (roughly
+   * speaking)
+   */
+  Execution get_prefix_before(EventHandle) const;
+
+  /**
+   * @brief Whether the event represented by `e1`
+   * "happens-before" the event represented by
+   * `e2` in the context of this execution
+   *
+   * In the terminology of the ODPOR paper,
+   * this function computes
+   *
+   * `e1 --->_E e2`
+   *
+   * where `E` is this execution
+   *
+   * @note: The happens-before relation computed by this
+   * execution is "coarse" in the sense that context-sensitive
+   * independence is not exploited. To include such context-sensitive
+   * dependencies requires a new method of keeping track of
+   * the happens-before procedure, which is nontrivial...
+   */
+  bool happens_before(EventHandle e1, EventHandle e2) const;
+
+  /**
+   * @brief Extends the execution by one more step
+   *
+   * Intutively, pushing a transition `t` onto execution `E`
+   * is equivalent to making the execution become (using the
+   * notation of [1]) `E.proc(t)` where `proc(t)` is the
+   * actor which executed transition `t`.
+   */
+  void push_transition(std::shared_ptr<Transition>);
+
+  /**
+   * @brief Extends the execution by a sequence of steps
+   */
+  void push_partial_execution(const PartialExecution&);
+};
+
+} // namespace simgrid::mc::odpor
+#endif
diff --git a/src/mc/explo/odpor/Execution_test.cpp b/src/mc/explo/odpor/Execution_test.cpp
new file mode 100644 (file)
index 0000000..364d9a0
--- /dev/null
@@ -0,0 +1,732 @@
+/* Copyright (c) 2017-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. */
+
+#include "src/3rd-party/catch.hpp"
+#include "src/mc/explo/odpor/Execution.hpp"
+#include "src/mc/explo/odpor/odpor_tests_private.hpp"
+#include "src/mc/transition/TransitionComm.hpp"
+
+using namespace simgrid::mc;
+using namespace simgrid::mc::odpor;
+using namespace simgrid::mc::udpor;
+
+TEST_CASE("simgrid::mc::odpor::Execution: Constructing Executions")
+{
+  Execution execution;
+  REQUIRE(execution.empty());
+  REQUIRE(execution.size() == 0);
+  REQUIRE_FALSE(execution.get_latest_event_handle().has_value());
+}
+
+TEST_CASE("simgrid::mc::odpor::Execution: Testing Happens-Before")
+{
+  SECTION("Example 1")
+  {
+    // We check each permutation for happens before
+    // among the given actions added to the execution
+    const auto a1 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a2 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto a3 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto a4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 4);
+
+    Execution execution;
+    execution.push_transition(a1);
+    execution.push_transition(a2);
+    execution.push_transition(a3);
+    execution.push_transition(a4);
+
+    SECTION("Happens-before is irreflexive")
+    {
+      REQUIRE_FALSE(execution.happens_before(0, 0));
+      REQUIRE_FALSE(execution.happens_before(1, 1));
+      REQUIRE_FALSE(execution.happens_before(2, 2));
+      REQUIRE_FALSE(execution.happens_before(3, 3));
+    }
+
+    SECTION("Happens-before values for each pair of events")
+    {
+      REQUIRE_FALSE(execution.happens_before(0, 1));
+      REQUIRE_FALSE(execution.happens_before(0, 2));
+      REQUIRE(execution.happens_before(0, 3));
+      REQUIRE_FALSE(execution.happens_before(1, 2));
+      REQUIRE_FALSE(execution.happens_before(1, 3));
+      REQUIRE_FALSE(execution.happens_before(2, 3));
+    }
+
+    SECTION("Happens-before is a subset of 'occurs-before' ")
+    {
+      REQUIRE_FALSE(execution.happens_before(1, 0));
+      REQUIRE_FALSE(execution.happens_before(2, 0));
+      REQUIRE_FALSE(execution.happens_before(3, 0));
+      REQUIRE_FALSE(execution.happens_before(2, 1));
+      REQUIRE_FALSE(execution.happens_before(3, 1));
+      REQUIRE_FALSE(execution.happens_before(3, 2));
+    }
+  }
+
+  SECTION("Example 2")
+  {
+    const auto a1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a2 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto a3 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto a4 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
+
+    // Notice that `a5` and `a6` are executed by the same actor; thus, although
+    // the actor is executing independent actions, each still "happen-before"
+    // the another
+
+    Execution execution;
+    execution.push_transition(a1);
+    execution.push_transition(a2);
+    execution.push_transition(a3);
+    execution.push_transition(a4);
+
+    SECTION("Happens-before is irreflexive")
+    {
+      REQUIRE_FALSE(execution.happens_before(0, 0));
+      REQUIRE_FALSE(execution.happens_before(1, 1));
+      REQUIRE_FALSE(execution.happens_before(2, 2));
+      REQUIRE_FALSE(execution.happens_before(3, 3));
+    }
+
+    SECTION("Happens-before values for each pair of events")
+    {
+      REQUIRE_FALSE(execution.happens_before(0, 1));
+      REQUIRE_FALSE(execution.happens_before(0, 2));
+      REQUIRE_FALSE(execution.happens_before(0, 3));
+      REQUIRE(execution.happens_before(1, 2));
+      REQUIRE(execution.happens_before(1, 3));
+      REQUIRE(execution.happens_before(2, 3));
+    }
+
+    SECTION("Happens-before is a subset of 'occurs-before'")
+    {
+      REQUIRE_FALSE(execution.happens_before(1, 0));
+      REQUIRE_FALSE(execution.happens_before(2, 0));
+      REQUIRE_FALSE(execution.happens_before(3, 0));
+      REQUIRE_FALSE(execution.happens_before(2, 1));
+      REQUIRE_FALSE(execution.happens_before(3, 1));
+      REQUIRE_FALSE(execution.happens_before(3, 2));
+    }
+  }
+
+  SECTION("Happens-before is transitively-closed")
+  {
+    SECTION("Example 1")
+    {
+      // Given a reversible race between events `e1` and `e3` in a simulation,
+      // we assert that `e5` would be eliminated from being contained in
+      // the sequence `notdep(e1, E)`
+      const auto e0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
+      const auto e1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
+      const auto e2 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
+      const auto e3 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
+      const auto e4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
+
+      Execution execution;
+      execution.push_partial_execution(PartialExecution{e0, e1, e2, e3, e4});
+      REQUIRE(execution.happens_before(0, 2));
+      REQUIRE(execution.happens_before(2, 4));
+      REQUIRE(execution.happens_before(0, 4));
+    }
+
+    SECTION("Stress testing transitivity of the happens-before relation")
+    {
+      // This test verifies that for each triple of events
+      // in the execution, for a modestly intersting one,
+      // that transitivity holds
+      const auto e0  = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+      const auto e1  = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+      const auto e2  = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+      const auto e3  = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 2, -10);
+      const auto e4  = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
+      const auto e5  = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 0);
+      const auto e6  = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 2, -5);
+      const auto e7  = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+      const auto e8  = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 0);
+      const auto e9  = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 2, -10);
+      const auto e10 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
+
+      Execution execution;
+      execution.push_partial_execution(PartialExecution{e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10});
+
+      const auto max_handle = execution.get_latest_event_handle();
+      for (Execution::EventHandle e_i = 0; e_i < max_handle; ++e_i) {
+        for (Execution::EventHandle e_j = 0; e_j < max_handle; ++e_j) {
+          for (Execution::EventHandle e_k = 0; e_k < max_handle; ++e_k) {
+            const bool e_i_before_e_j = execution.happens_before(e_i, e_j);
+            const bool e_j_before_e_k = execution.happens_before(e_j, e_k);
+            const bool e_i_before_e_k = execution.happens_before(e_i, e_k);
+            // Logical equivalent of `e_i_before_e_j ^ e_j_before_e_k --> e_i_before_e_k`
+            REQUIRE((!(e_i_before_e_j and e_j_before_e_k) or e_i_before_e_k));
+          }
+        }
+      }
+    }
+  }
+}
+
+TEST_CASE("simgrid::mc::odpor::Execution: Testing Racing Events and Initials")
+{
+  SECTION("Example 1")
+  {
+    const auto a1 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto a3 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a4 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a5 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
+
+    Execution execution;
+    execution.push_transition(a1);
+    execution.push_transition(a2);
+    execution.push_transition(a3);
+    execution.push_transition(a4);
+    execution.push_transition(a5);
+
+    // Nothing comes before event 0
+    REQUIRE(execution.get_racing_events_of(0) == std::unordered_set<Execution::EventHandle>{});
+
+    // Events 0 and 1 are independent
+    REQUIRE(execution.get_racing_events_of(1) == std::unordered_set<Execution::EventHandle>{});
+
+    // 2 and 1 are executed by different actors and happen right after each other
+    REQUIRE(execution.get_racing_events_of(2) == std::unordered_set<Execution::EventHandle>{1});
+
+    // Although a3 and a4 are dependent, they are executed by the same actor
+    REQUIRE(execution.get_racing_events_of(3) == std::unordered_set<Execution::EventHandle>{});
+
+    // Event 4 is in a race with neither event 0 nor event 2 since those events
+    // "happen-before" event 3 with which event 4 races
+    //
+    // Furthermore, event 1 is run by the same actor and thus also is not in a race.
+    // Hence, only event 3 races with event 4
+    REQUIRE(execution.get_racing_events_of(4) == std::unordered_set<Execution::EventHandle>{3});
+  }
+
+  SECTION("Example 2: Events with multiple races")
+  {
+    const auto a1 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto a3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
+
+    Execution execution;
+    execution.push_transition(a1);
+    execution.push_transition(a2);
+    execution.push_transition(a3);
+    execution.push_transition(a4);
+
+    // Nothing comes before event 0
+    REQUIRE(execution.get_racing_events_of(0) == std::unordered_set<Execution::EventHandle>{});
+
+    // Events 0 and 1 are independent
+    REQUIRE(execution.get_racing_events_of(1) == std::unordered_set<Execution::EventHandle>{});
+
+    // Event 2 is independent with event 1 and run by the same actor as event 0
+    REQUIRE(execution.get_racing_events_of(2) == std::unordered_set<Execution::EventHandle>{});
+
+    // All events are dependent with event 3, but event 0 "happens-before" event 2
+    REQUIRE(execution.get_racing_events_of(3) == std::unordered_set<Execution::EventHandle>{1, 2});
+
+    SECTION("Check initials with respect to event 1")
+    {
+      // First, note that v := notdep(1, execution).p == {e2}.{e3} == {e2, e3}
+      // Since e2 -->_E e3, actor 3 is not an initial for E' := pre(1, execution)
+      // with respect to `v`. e2, however, has nothing happening before it in dom_E(v),
+      // so it is an initial of E' wrt. `v`
+      const auto initial_wrt_event1 = execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{});
+      REQUIRE(initial_wrt_event1 == std::unordered_set<aid_t>{1});
+    }
+
+    SECTION("Check initials with respect to event 2")
+    {
+      // First, note that v := notdep(1, execution).p == {}.{e3} == {e3}
+      // e3 has nothing happening before it in dom_E(v), so it is an initial
+      // of E' wrt. `v`
+      const auto initial_wrt_event2 = execution.get_missing_source_set_actors_from(2, std::unordered_set<aid_t>{});
+      REQUIRE(initial_wrt_event2 == std::unordered_set<aid_t>{3});
+    }
+  }
+
+  SECTION("Example 3: Testing 'Lock' Example")
+  {
+    // In this example, `e0` and `e1` are lock actions that
+    // are in a race. We assert that
+    const auto e0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto e1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto e2 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto e3 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto e4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
+
+    Execution execution;
+    execution.push_transition(e0);
+    execution.push_transition(e1);
+    execution.push_transition(e2);
+    execution.push_transition(e3);
+    execution.push_transition(e4);
+    REQUIRE(execution.get_racing_events_of(4) == std::unordered_set<Execution::EventHandle>{0});
+  }
+
+  SECTION("Example 4: Indirect Races")
+  {
+    const auto e0 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto e1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto e2 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto e3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto e4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 6);
+    const auto e5 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto e6 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto e7 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto e8 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
+    const auto e9 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
+
+    Execution execution(PartialExecution{e0, e1, e2, e3, e4, e5, e6, e7, e8, e9});
+
+    // Nothing comes before event 0
+    REQUIRE(execution.get_racing_events_of(0) == std::unordered_set<Execution::EventHandle>{});
+
+    // Events 0 and 1 are independent
+    REQUIRE(execution.get_racing_events_of(1) == std::unordered_set<Execution::EventHandle>{});
+
+    // Events 1 and 2 are independent
+    REQUIRE(execution.get_racing_events_of(2) == std::unordered_set<Execution::EventHandle>{});
+
+    // Events 1 and 3 are independent; the rest are executed by the same actor
+    REQUIRE(execution.get_racing_events_of(3) == std::unordered_set<Execution::EventHandle>{});
+
+    // 1. Events 3 and 4 race
+    // 2. Events 2 and 4 do NOT race since 2 --> 3 --> 4
+    // 3. Events 1 and 4 do NOT race since 1 is independent of 4
+    // 4. Events 0 and 4 do NOT race since 0 --> 2 --> 4
+    REQUIRE(execution.get_racing_events_of(4) == std::unordered_set<Execution::EventHandle>{3});
+
+    // Events 4 and 5 race; and because everyone before 4 (including 3) either
+    // a) happens-before, b) races, or c) does not race with 4, 4 is the race
+    REQUIRE(execution.get_racing_events_of(5) == std::unordered_set<Execution::EventHandle>{4});
+
+    // The same logic that applied to event 5 applies to event 6
+    REQUIRE(execution.get_racing_events_of(6) == std::unordered_set<Execution::EventHandle>{5});
+
+    // The same logic applies, except that this time since events 6 and 7 are run
+    // by the same actor, they don'tt actually race with one another
+    REQUIRE(execution.get_racing_events_of(7) == std::unordered_set<Execution::EventHandle>{});
+
+    // Event 8 is independent with everything
+    REQUIRE(execution.get_racing_events_of(8) == std::unordered_set<Execution::EventHandle>{});
+
+    // Event 9 is independent with events 7 and 8; event 6, however, is in race with 9.
+    // The same logic above eliminates events before 6
+    REQUIRE(execution.get_racing_events_of(9) == std::unordered_set<Execution::EventHandle>{6});
+  }
+
+  SECTION("Example 5: Stress testing race detection")
+  {
+    const auto e0  = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto e1  = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto e2  = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto e3  = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 2, -10);
+    const auto e4  = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto e5  = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 0);
+    const auto e6  = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 2, -5);
+    const auto e7  = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 1, -5);
+    const auto e8  = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 0, 4);
+    const auto e9  = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 2, -10);
+    const auto e10 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
+
+    Execution execution(PartialExecution{e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10});
+
+    // Nothing comes before event 0
+    CHECK(execution.get_racing_events_of(0) == std::unordered_set<Execution::EventHandle>{});
+
+    // Events 0 and 1 are independent
+    CHECK(execution.get_racing_events_of(1) == std::unordered_set<Execution::EventHandle>{});
+
+    // Events 1 and 2 are executed by the same actor, even though they are dependent
+    CHECK(execution.get_racing_events_of(2) == std::unordered_set<Execution::EventHandle>{});
+
+    // Events 2 and 3 are independent while events 1 and 2 are dependent
+    CHECK(execution.get_racing_events_of(3) == std::unordered_set<Execution::EventHandle>{1});
+
+    // Event 4 is independent with everything so it can never be in a race
+    CHECK(execution.get_racing_events_of(4) == std::unordered_set<Execution::EventHandle>{});
+
+    // Event 5 is independent with event 4. Since events 2 and 3 are dependent with event 5,
+    // but are independent of each other, both events are in a race with event 5
+    CHECK(execution.get_racing_events_of(5) == std::unordered_set<Execution::EventHandle>{2, 3});
+
+    // Events 5 and 6 are dependent. Everyone before 5 who's dependent with 5
+    // cannot be in a race with 6; everyone before 5 who's independent with 5
+    // could not race with 6
+    CHECK(execution.get_racing_events_of(6) == std::unordered_set<Execution::EventHandle>{5});
+
+    // Same goes for event 7 as for 6
+    CHECK(execution.get_racing_events_of(7) == std::unordered_set<Execution::EventHandle>{6});
+
+    // Events 5 and 8 are both run by the same actor; events in-between are independent
+    CHECK(execution.get_racing_events_of(8) == std::unordered_set<Execution::EventHandle>{});
+
+    // Event 6 blocks event 9 from racing with event 6
+    CHECK(execution.get_racing_events_of(9) == std::unordered_set<Execution::EventHandle>{});
+
+    // Event 10 is independent with everything so it can never be in a race
+    CHECK(execution.get_racing_events_of(10) == std::unordered_set<Execution::EventHandle>{});
+  }
+
+  SECTION("Example with many races for one event")
+  {
+    const auto e0 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto e1 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto e2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto e3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+    const auto e4 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 5);
+    const auto e5 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 6);
+    const auto e6 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 7);
+
+    Execution execution(PartialExecution{e0, e1, e2, e3, e4, e5, e6});
+    REQUIRE(execution.get_racing_events_of(6) == std::unordered_set<Execution::EventHandle>{0, 1, 2, 3, 4, 5});
+  }
+}
+
+TEST_CASE("simgrid::mc::odpor::Execution: Independence Tests")
+{
+  SECTION("Complete independence")
+  {
+    // Every transition is independent with every other and run by different actors. Hopefully
+    // we say that the events are independent with each other...
+    const auto a0 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto a2 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto a3 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
+    const auto a4 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 5);
+    const auto a5 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 6);
+    const auto a6 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 7);
+    const auto a7 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 7);
+    Execution execution(PartialExecution{a0, a1, a2, a3});
+
+    REQUIRE(execution.is_independent_with_execution_of(PartialExecution{a4, a5}, a6));
+    REQUIRE(execution.is_independent_with_execution_of(PartialExecution{a6, a5}, a1));
+    REQUIRE(execution.is_independent_with_execution_of(PartialExecution{a6, a1}, a0));
+    REQUIRE(Execution().is_independent_with_execution_of(PartialExecution{a7, a7, a1}, a3));
+    REQUIRE(Execution().is_independent_with_execution_of(PartialExecution{a4, a0, a1}, a3));
+    REQUIRE(Execution().is_independent_with_execution_of(PartialExecution{a0, a7, a1}, a2));
+
+    // In this case, we notice that `a6` and `a7` are executed by the same actor.
+    // Hence, a6 cannot be independent with extending the execution with a4.a5.a7.
+    // Notice that we are treating *a6* as the next step of actor 7 (that is what we
+    // supplied as an argument)
+    REQUIRE_FALSE(execution.is_independent_with_execution_of(PartialExecution{a4, a5, a7}, a6));
+  }
+
+  SECTION("Independence is trivial with an empty extension")
+  {
+    REQUIRE(Execution().is_independent_with_execution_of(
+        PartialExecution{}, std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1)));
+  }
+
+  SECTION("Dependencies stopping independence from being possible")
+  {
+    const auto a0    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a1    = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a2    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto a3    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+    const auto a4    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+    const auto a5    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto a6    = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto a7    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a8    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+    const auto indep = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+    Execution execution(PartialExecution{a0, a1, a2, a3});
+
+    // We see that although `a4` comes after `a1` with which it is dependent, it
+    // would come before "a6" in the partial execution `w`, so it would not be independent
+    REQUIRE_FALSE(execution.is_independent_with_execution_of(PartialExecution{a5, a6, a7}, a4));
+
+    // Removing `a6` from the picture, though, gives us the independence we're looking for.
+    REQUIRE(execution.is_independent_with_execution_of(PartialExecution{a5, a7}, a4));
+
+    // BUT, we we ask about a transition which is run by the same actor, even if they would be
+    // independent otherwise, we again lose independence
+    REQUIRE_FALSE(execution.is_independent_with_execution_of(PartialExecution{a5, a7, a8}, a4));
+
+    // This is a more interesting case:
+    // `indep` clearly is independent with the rest of the series, even though
+    // there are dependencies among the other events in the partial execution
+    REQUIRE(Execution().is_independent_with_execution_of(PartialExecution{a1, a6, a7}, indep));
+
+    // This ensures that independence is trivial with an empty partial execution
+    REQUIRE(execution.is_independent_with_execution_of(PartialExecution{}, a1));
+  }
+
+  SECTION("More interesting dependencies stopping independence")
+  {
+    const auto e0 = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 1, 5);
+    const auto e1 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto e2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto e3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto e4 = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 3, 5);
+    const auto e5 = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 4, 4);
+    Execution execution(PartialExecution{e0, e1, e2, e3, e4, e5});
+
+    SECTION("Action run by same actor disqualifies independence")
+    {
+      const auto w_1 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+      const auto w_2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+      const auto w_3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+      const auto w_4 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
+      const auto w   = PartialExecution{w_1, w_2, w_3};
+
+      const auto actor4_action  = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
+      const auto actor4_action2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+
+      // Action `actor4_action` is independent with everything EXCEPT the last transition
+      // which is executed by the same actor
+      execution.is_independent_with_execution_of(w, actor4_action);
+
+      // Action `actor4_action2` is independent with everything
+      // EXCEPT the last transition which is executed by the same actor
+      execution.is_independent_with_execution_of(w, actor4_action);
+    }
+  }
+}
+
+TEST_CASE("simgrid::mc::odpor::Execution: Initials Test")
+{
+  const auto a0    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+  const auto a1    = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+  const auto a2    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 3);
+  const auto a3    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+  const auto a4    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+  const auto a5    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
+  const auto a6    = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
+  const auto a7    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+  const auto a8    = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+  const auto indep = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+  Execution execution(PartialExecution{a0, a1, a2, a3});
+
+  SECTION("Initials trivial with empty executions")
+  {
+    // There are no initials for an empty extension since
+    // a requirement is that the actor be contained in the
+    // extension itself
+    REQUIRE_FALSE(execution.is_initial_after_execution_of(PartialExecution{}, 0));
+    REQUIRE_FALSE(execution.is_initial_after_execution_of(PartialExecution{}, 1));
+    REQUIRE_FALSE(execution.is_initial_after_execution_of(PartialExecution{}, 2));
+    REQUIRE_FALSE(Execution().is_initial_after_execution_of(PartialExecution{}, 0));
+    REQUIRE_FALSE(Execution().is_initial_after_execution_of(PartialExecution{}, 1));
+    REQUIRE_FALSE(Execution().is_initial_after_execution_of(PartialExecution{}, 2));
+  }
+
+  SECTION("The first actor is always an initial")
+  {
+    // Even in the case that the action is dependent with every
+    // other, if it is the first action to occur as part of the
+    // extension of the execution sequence, by definition it is
+    // an initial (recall that initials intuitively tell you which
+    // actions can be run starting from an execution `E`; if we claim
+    // to witness `E.w`, then clearly at least the first step of `w` is an initial)
+    REQUIRE(execution.is_initial_after_execution_of(PartialExecution{a3}, a3->aid_));
+    REQUIRE(execution.is_initial_after_execution_of(PartialExecution{a2, a3, a6}, a2->aid_));
+    REQUIRE(execution.is_initial_after_execution_of(PartialExecution{a6, a1, a0}, a6->aid_));
+    REQUIRE(Execution().is_initial_after_execution_of(PartialExecution{a0, a1, a2, a3}, a0->aid_));
+    REQUIRE(Execution().is_initial_after_execution_of(PartialExecution{a5, a2, a8, a7, a6, a0}, a5->aid_));
+    REQUIRE(Execution().is_initial_after_execution_of(PartialExecution{a8, a7, a1}, a8->aid_));
+  }
+
+  SECTION("Example: Disqualified and re-qualified after switching ordering")
+  {
+    // Even though actor `2` executes an independent
+    // transition later on, it is blocked since its first transition
+    // is dependent with actor 1's transition
+    REQUIRE_FALSE(Execution().is_initial_after_execution_of(PartialExecution{a1, a5, indep}, 2));
+
+    // However, if actor 2 executes its independent action first, it DOES become an initial
+    REQUIRE(Execution().is_initial_after_execution_of(PartialExecution{a1, indep, a5}, 2));
+  }
+
+  SECTION("Example: Large partial executions")
+  {
+    // The record trace is `1 3 4 4 3 1 4 2`
+
+    // Actor 1 starts the execution
+    REQUIRE(Execution().is_initial_after_execution_of(PartialExecution{a1, a2, a3, a4, a6, a7, a8, indep}, 1));
+
+    // Actor 2 all the way at the end is independent with everybody: despite
+    // the tangle that comes before it, we can more it to the front with no issue
+    REQUIRE(Execution().is_initial_after_execution_of(PartialExecution{a1, a2, a3, a4, a6, a7, a8, indep}, 2));
+
+    // Actor 3 is eliminated since `a1` is dependent with `a2`
+    REQUIRE_FALSE(Execution().is_initial_after_execution_of(PartialExecution{a1, a2, a3, a4, a6, a7, a8, indep}, 3));
+
+    // Likewise with actor 4
+    REQUIRE_FALSE(Execution().is_initial_after_execution_of(PartialExecution{a1, a2, a3, a4, a6, a7, a8, indep}, 4));
+  }
+
+  SECTION("Example: Stress tests for initials computation")
+  {
+    const auto v_1 = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 1, 3);
+    const auto v_2 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto v_3 = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 2, 3);
+    const auto v_4 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto v_5 = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 3, 8);
+    const auto v_6 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto v_7 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto v_8 = std::make_shared<DependentIfSameValueAction>(Transition::Type::UNKNOWN, 4, 3);
+    const auto v   = PartialExecution{v_1, v_2, v_3, v_4};
+
+    // Actor 1 being the first actor in the expansion, it is clearly an initial
+    REQUIRE(Execution().is_initial_after_execution_of(v, 1));
+
+    // Actor 2 can't be switched before actor 1 without creating an trace
+    // that leads to a different state than that of `E.v := ().v := v`
+    REQUIRE_FALSE(Execution().is_initial_after_execution_of(v, 2));
+
+    // The first action of Actor 3 is independent with what comes before it, so it can
+    // be moved forward. Note that this is the case even though it later executes and action
+    // that's dependent with most everyone else
+    REQUIRE(Execution().is_initial_after_execution_of(v, 3));
+
+    // Actor 4 is blocked actor 3's second action `v_7`
+    REQUIRE_FALSE(Execution().is_initial_after_execution_of(v, 4));
+  }
+}
+
+TEST_CASE("simgrid::mc::odpor::Execution: SDPOR Backtracking Simulation")
+{
+  // This test case assumes that each detected race is detected to also
+  // be reversible. For each reversible
+  const auto e0 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+  const auto e1 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+  const auto e2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 3);
+  const auto e3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+  const auto e4 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+  const auto e5 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2);
+  const auto e6 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
+  const auto e7 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+
+  Execution execution;
+
+  execution.push_transition(e0);
+  REQUIRE(execution.get_racing_events_of(0) == std::unordered_set<Execution::EventHandle>{});
+
+  execution.push_transition(e1);
+  REQUIRE(execution.get_racing_events_of(1) == std::unordered_set<Execution::EventHandle>{});
+
+  // Actor 3, since it starts the extension from event 1, clearly is an initial from there
+  execution.push_transition(e2);
+  REQUIRE(execution.get_racing_events_of(2) == std::unordered_set<Execution::EventHandle>{1});
+  CHECK(execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{}) == std::unordered_set<aid_t>{3});
+  CHECK(execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{4, 5}) ==
+        std::unordered_set<aid_t>{3});
+  CHECK(execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{3}) == std::unordered_set<aid_t>{});
+
+  // e1 and e3 are in an reversible race. Actor 4 is not hindered from being moved to
+  // the front since e2 is independent with e3; hence, it is an initial
+  execution.push_transition(e3);
+  REQUIRE(execution.get_racing_events_of(3) == std::unordered_set<Execution::EventHandle>{1});
+  CHECK(execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{}) == std::unordered_set<aid_t>{4});
+  CHECK(execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{3, 5}) ==
+        std::unordered_set<aid_t>{4});
+  CHECK(execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{4}) == std::unordered_set<aid_t>{});
+
+  // e4 is not in a race since e3 is run by the same actor and occurs before e4
+  execution.push_transition(e4);
+  REQUIRE(execution.get_racing_events_of(4) == std::unordered_set<Execution::EventHandle>{});
+
+  // e5 is independent with everything between e1 and e5, and `proc(e5) == 2`
+  execution.push_transition(e5);
+  REQUIRE(execution.get_racing_events_of(5) == std::unordered_set<Execution::EventHandle>{1});
+  CHECK(execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{}) == std::unordered_set<aid_t>{2});
+  CHECK(execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{4, 5}) ==
+        std::unordered_set<aid_t>{2});
+  CHECK(execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{2}) == std::unordered_set<aid_t>{});
+
+  // Event 6 has two races: one with event 4 and one with event 5. In the latter race, actor 3 follows
+  // immediately after and so is evidently a source set actor; in the former race, only actor 2 can
+  // be brought to the front of the queue
+  execution.push_transition(e6);
+  REQUIRE(execution.get_racing_events_of(6) == std::unordered_set<Execution::EventHandle>{4, 5});
+  CHECK(execution.get_missing_source_set_actors_from(4, std::unordered_set<aid_t>{}) == std::unordered_set<aid_t>{2});
+  CHECK(execution.get_missing_source_set_actors_from(4, std::unordered_set<aid_t>{6, 7}) ==
+        std::unordered_set<aid_t>{2});
+  CHECK(execution.get_missing_source_set_actors_from(4, std::unordered_set<aid_t>{2}) == std::unordered_set<aid_t>{});
+  CHECK(execution.get_missing_source_set_actors_from(5, std::unordered_set<aid_t>{}) == std::unordered_set<aid_t>{3});
+  CHECK(execution.get_missing_source_set_actors_from(5, std::unordered_set<aid_t>{6, 7}) ==
+        std::unordered_set<aid_t>{3});
+  CHECK(execution.get_missing_source_set_actors_from(5, std::unordered_set<aid_t>{3}) == std::unordered_set<aid_t>{});
+
+  // Finally, event e7 races with e6 and `proc(e7) = 1` is brought forward
+  execution.push_transition(e7);
+  REQUIRE(execution.get_racing_events_of(7) == std::unordered_set<Execution::EventHandle>{6});
+  CHECK(execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{}) == std::unordered_set<aid_t>{1});
+  CHECK(execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{4, 5}) ==
+        std::unordered_set<aid_t>{1});
+  CHECK(execution.get_missing_source_set_actors_from(1, std::unordered_set<aid_t>{1}) == std::unordered_set<aid_t>{});
+}
+
+TEST_CASE("simgrid::mc::odpor::Execution: ODPOR Smallest Sequence Tests")
+{
+  const auto a0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
+  const auto a1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
+  const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 3);
+  const auto a3 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+  const auto a4 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+  const auto a5 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+
+  Execution execution;
+  execution.push_transition(a0);
+  execution.push_transition(a1);
+  execution.push_transition(a2);
+  execution.push_transition(a3);
+  execution.push_transition(a4);
+  execution.push_transition(a5);
+
+  SECTION("Equivalent insertions")
+  {
+    SECTION("Example: Eliminated through independence")
+    {
+      // TODO: Is this even a sensible question to ask if the two sequences
+      // don't agree upon what each actor executed after `E`?
+      const auto insertion =
+          Execution().get_shortest_odpor_sq_subset_insertion(PartialExecution{a1, a2}, PartialExecution{a2, a5});
+      REQUIRE(insertion.has_value());
+      REQUIRE(insertion.value() == PartialExecution{});
+    }
+
+    SECTION("Exact match yields empty set")
+    {
+      const auto insertion =
+          Execution().get_shortest_odpor_sq_subset_insertion(PartialExecution{a1, a2}, PartialExecution{a1, a2});
+      REQUIRE(insertion.has_value());
+      REQUIRE(insertion.value() == PartialExecution{});
+    }
+  }
+
+  SECTION("Match against empty executions")
+  {
+    SECTION("Empty `v` trivially yields `w`")
+    {
+      auto insertion =
+          Execution().get_shortest_odpor_sq_subset_insertion(PartialExecution{}, PartialExecution{a1, a5, a2});
+      REQUIRE(insertion.has_value());
+      REQUIRE(insertion.value() == PartialExecution{a1, a5, a2});
+
+      insertion = execution.get_shortest_odpor_sq_subset_insertion(PartialExecution{}, PartialExecution{a1, a5, a2});
+      REQUIRE(insertion.has_value());
+      REQUIRE(insertion.value() == PartialExecution{a1, a5, a2});
+    }
+
+    SECTION("Empty `w` yields empty execution")
+    {
+      auto insertion =
+          Execution().get_shortest_odpor_sq_subset_insertion(PartialExecution{a1, a4, a5}, PartialExecution{});
+      REQUIRE(insertion.has_value());
+      REQUIRE(insertion.value() == PartialExecution{});
+
+      insertion = execution.get_shortest_odpor_sq_subset_insertion(PartialExecution{a1, a5, a2}, PartialExecution{});
+      REQUIRE(insertion.has_value());
+      REQUIRE(insertion.value() == PartialExecution{});
+    }
+  }
+}
diff --git a/src/mc/explo/odpor/ReversibleRaceCalculator.cpp b/src/mc/explo/odpor/ReversibleRaceCalculator.cpp
new file mode 100644 (file)
index 0000000..efab9d3
--- /dev/null
@@ -0,0 +1,199 @@
+/* Copyright (c) 2008-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. */
+
+#include "src/mc/explo/odpor/ReversibleRaceCalculator.hpp"
+#include "src/mc/explo/odpor/Execution.hpp"
+
+#include <functional>
+#include <unordered_map>
+#include <xbt/asserts.h>
+#include <xbt/ex.h>
+
+namespace simgrid::mc::odpor {
+
+bool ReversibleRaceCalculator::is_race_reversible(const Execution& E, Execution::EventHandle e1,
+                                                  Execution::EventHandle e2)
+{
+  using Action     = Transition::Type;
+  using Handler    = std::function<bool(const Execution&, Execution::EventHandle, const Transition*)>;
+  using HandlerMap = std::unordered_map<Action, Handler>;
+
+  const static HandlerMap handlers =
+      HandlerMap{{Action::ACTOR_JOIN, &ReversibleRaceCalculator::is_race_reversible_ActorJoin},
+                 {Action::BARRIER_ASYNC_LOCK, &ReversibleRaceCalculator::is_race_reversible_BarrierAsyncLock},
+                 {Action::BARRIER_WAIT, &ReversibleRaceCalculator::is_race_reversible_BarrierWait},
+                 {Action::COMM_ASYNC_SEND, &ReversibleRaceCalculator::is_race_reversible_CommSend},
+                 {Action::COMM_ASYNC_RECV, &ReversibleRaceCalculator::is_race_reversible_CommRecv},
+                 {Action::COMM_TEST, &ReversibleRaceCalculator::is_race_reversible_CommTest},
+                 {Action::COMM_WAIT, &ReversibleRaceCalculator::is_race_reversible_CommWait},
+                 {Action::MUTEX_ASYNC_LOCK, &ReversibleRaceCalculator::is_race_reversible_MutexAsyncLock},
+                 {Action::MUTEX_TEST, &ReversibleRaceCalculator::is_race_reversible_MutexTest},
+                 {Action::MUTEX_TRYLOCK, &ReversibleRaceCalculator::is_race_reversible_MutexTrylock},
+                 {Action::MUTEX_UNLOCK, &ReversibleRaceCalculator::is_race_reversible_MutexUnlock},
+                 {Action::MUTEX_WAIT, &ReversibleRaceCalculator::is_race_reversible_MutexWait},
+                 {Action::OBJECT_ACCESS, &ReversibleRaceCalculator::is_race_reversible_ObjectAccess},
+                 {Action::RANDOM, &ReversibleRaceCalculator::is_race_reversible_Random},
+                 {Action::SEM_ASYNC_LOCK, &ReversibleRaceCalculator::is_race_reversible_SemAsyncLock},
+                 {Action::SEM_UNLOCK, &ReversibleRaceCalculator::is_race_reversible_SemUnlock},
+                 {Action::SEM_WAIT, &ReversibleRaceCalculator::is_race_reversible_SemWait},
+                 {Action::TESTANY, &ReversibleRaceCalculator::is_race_reversible_TestAny},
+                 {Action::WAITANY, &ReversibleRaceCalculator::is_race_reversible_WaitAny}};
+
+  const auto e2_action = E.get_transition_for_handle(e2);
+  if (const auto handler = handlers.find(e2_action->type_); handler != handlers.end()) {
+    return handler->second(E, e1, e2_action);
+  } else {
+    xbt_die("There is currently no specialized computation for the transition "
+            "'%s' for computing reversible races in ODPOR, so the model checker cannot "
+            "determine how to proceed. Please submit a bug report requesting "
+            "that the transition be supported in SimGrid using ODPPR and consider "
+            "using the other model-checking algorithms supported by SimGrid instead "
+            "in the meantime",
+            e2_action->to_string().c_str());
+  }
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_ActorJoin(const Execution&, Execution::EventHandle e1,
+                                                            const Transition* e2)
+{
+  // ActorJoin races with another event iff its target `T` is the same as
+  // the actor executing the other transition. Clearly, then, we could not join
+  // on that actor `T` and then run a transition by `T`, so no race is reversible
+  return false;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_BarrierAsyncLock(const Execution&, Execution::EventHandle e1,
+                                                                   const Transition* e2)
+{
+  // BarrierAsyncLock is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_BarrierWait(const Execution& E, Execution::EventHandle e1,
+                                                              const Transition* e2)
+{
+  // If the other event is a barrier lock event, then we
+  // are not reversible; otherwise we are reversible.
+  const auto e1_action = E.get_transition_for_handle(e1)->type_;
+  return e1_action != Transition::Type::BARRIER_ASYNC_LOCK;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_CommRecv(const Execution&, Execution::EventHandle e1,
+                                                           const Transition* e2)
+{
+  // CommRecv is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_CommSend(const Execution&, Execution::EventHandle e1,
+                                                           const Transition* e2)
+{
+  // CommSend is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_CommWait(const Execution& E, Execution::EventHandle e1,
+                                                           const Transition* e2)
+{
+  // If the other event is a communication event, then we
+  // are not reversible; otherwise we are reversible.
+  const auto e1_action = E.get_transition_for_handle(e1)->type_;
+  return e1_action != Transition::Type::COMM_ASYNC_SEND and e1_action != Transition::Type::COMM_ASYNC_RECV;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_CommTest(const Execution&, Execution::EventHandle e1,
+                                                           const Transition* e2)
+{
+  // CommTest is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_MutexAsyncLock(const Execution&, Execution::EventHandle e1,
+                                                                 const Transition* e2)
+{
+  // MutexAsyncLock is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_MutexTest(const Execution&, Execution::EventHandle e1,
+                                                            const Transition* e2)
+{
+  // MutexTest is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_MutexTrylock(const Execution&, Execution::EventHandle e1,
+                                                               const Transition* e2)
+{
+  // MutexTrylock is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_MutexUnlock(const Execution&, Execution::EventHandle e1,
+                                                              const Transition* e2)
+{
+  // MutexUnlock is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_MutexWait(const Execution& E, Execution::EventHandle e1,
+                                                            const Transition* e2)
+{
+  // TODO: Get the semantics correct here
+  const auto e1_action = E.get_transition_for_handle(e1)->type_;
+  return e1_action != Transition::Type::MUTEX_ASYNC_LOCK and e1_action != Transition::Type::MUTEX_UNLOCK;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_SemAsyncLock(const Execution&, Execution::EventHandle e1,
+                                                               const Transition* e2)
+{
+  // SemAsyncLock is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_SemUnlock(const Execution&, Execution::EventHandle e1,
+                                                            const Transition* e2)
+{
+  // SemUnlock is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_SemWait(const Execution&, Execution::EventHandle e1,
+                                                          const Transition* e2)
+{
+  // TODO: Get the semantics correct here
+  return false;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_ObjectAccess(const Execution&, Execution::EventHandle e1,
+                                                               const Transition* e2)
+{
+  // Object access is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_Random(const Execution&, Execution::EventHandle e1,
+                                                         const Transition* e2)
+{
+  // Random is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_TestAny(const Execution&, Execution::EventHandle e1,
+                                                          const Transition* e2)
+{
+  // TestAny is always enabled
+  return true;
+}
+
+bool ReversibleRaceCalculator::is_race_reversible_WaitAny(const Execution&, Execution::EventHandle e1,
+                                                          const Transition* e2)
+{
+  // TODO: We need to check if any of the transitions
+  // waited on occurred before `e1`
+  return false;
+}
+
+} // namespace simgrid::mc::odpor
\ No newline at end of file
diff --git a/src/mc/explo/odpor/ReversibleRaceCalculator.hpp b/src/mc/explo/odpor/ReversibleRaceCalculator.hpp
new file mode 100644 (file)
index 0000000..5135bd2
--- /dev/null
@@ -0,0 +1,58 @@
+/* Copyright (c) 2007-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. */
+
+#ifndef SIMGRID_MC_ODPOR_REVERSIBLE_RACE_CALCULATOR_HPP
+#define SIMGRID_MC_ODPOR_REVERSIBLE_RACE_CALCULATOR_HPP
+
+#include "src/mc/explo/odpor/Execution.hpp"
+#include "src/mc/explo/odpor/odpor_forward.hpp"
+#include "src/mc/transition/Transition.hpp"
+#include "src/mc/transition/TransitionActorJoin.hpp"
+#include "src/mc/transition/TransitionAny.hpp"
+#include "src/mc/transition/TransitionComm.hpp"
+#include "src/mc/transition/TransitionObjectAccess.hpp"
+#include "src/mc/transition/TransitionRandom.hpp"
+#include "src/mc/transition/TransitionSynchro.hpp"
+
+#include <memory>
+
+namespace simgrid::mc::odpor {
+
+/**
+ * @brief Computes whether a race between two events
+ * in a given execution is a reversible race.
+ *
+ * @note: All of the methods assume that there is
+ * indeed a race between the two events in the
+ * execution; indeed, the question the method answers
+ * is only sensible in the context of a race
+ */
+struct ReversibleRaceCalculator final {
+  static bool is_race_reversible_ActorJoin(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_BarrierAsyncLock(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_BarrierWait(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_CommRecv(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_CommSend(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_CommWait(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_CommTest(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_MutexAsyncLock(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_MutexTest(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_MutexTrylock(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_MutexUnlock(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_MutexWait(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_SemAsyncLock(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_SemUnlock(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_SemWait(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_ObjectAccess(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_Random(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_TestAny(const Execution&, Execution::EventHandle e1, const Transition* e2);
+  static bool is_race_reversible_WaitAny(const Execution&, Execution::EventHandle e1, const Transition* e2);
+
+public:
+  static bool is_race_reversible(const Execution&, Execution::EventHandle e1, Execution::EventHandle e2);
+};
+
+} // namespace simgrid::mc::odpor
+#endif
diff --git a/src/mc/explo/odpor/WakeupTree.cpp b/src/mc/explo/odpor/WakeupTree.cpp
new file mode 100644 (file)
index 0000000..7b73e6c
--- /dev/null
@@ -0,0 +1,219 @@
+/* Copyright (c) 2008-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. */
+
+#include "src/mc/explo/odpor/WakeupTree.hpp"
+#include "src/mc/explo/odpor/Execution.hpp"
+#include "xbt/asserts.h"
+#include "xbt/string.hpp"
+
+#include <algorithm>
+#include <exception>
+#include <queue>
+
+namespace simgrid::mc::odpor {
+
+void WakeupTreeNode::add_child(WakeupTreeNode* node)
+{
+  this->children_.push_back(node);
+  node->parent_ = this;
+}
+
+PartialExecution WakeupTreeNode::get_sequence() const
+{
+  // TODO: Prevent having to compute this at the node level
+  // and instead track this with the iterator
+  PartialExecution seq_;
+  const WakeupTreeNode* cur_node = this;
+  while (cur_node != nullptr and !cur_node->is_root()) {
+    seq_.push_front(cur_node->action_);
+    cur_node = cur_node->parent_;
+  }
+  return seq_;
+}
+
+void WakeupTreeNode::detatch_from_parent()
+{
+  if (parent_ != nullptr) {
+    // TODO: There may be a better method
+    // of keeping track of a node's reference to
+    // its parent, perhaps keeping track
+    // of a std::list<>::iterator instead.
+    // This would allow us to detach a node
+    // in O(1) instead of O(|children|) time
+    parent_->children_.remove(this);
+  }
+}
+
+WakeupTree::WakeupTree() : WakeupTree(std::unique_ptr<WakeupTreeNode>(new WakeupTreeNode({}))) {}
+WakeupTree::WakeupTree(std::unique_ptr<WakeupTreeNode> root) : root_(root.get())
+{
+  this->insert_node(std::move(root));
+}
+
+std::vector<std::string> WakeupTree::get_single_process_texts() const
+{
+  std::vector<std::string> trace;
+  for (const auto* child : root_->children_) {
+    const auto t       = child->get_action();
+    const auto message = xbt::string_printf("Actor %ld: %s", t->aid_, t->to_string(true).c_str());
+    trace.push_back(std::move(message));
+  }
+  return trace;
+}
+
+std::optional<aid_t> WakeupTree::get_min_single_process_actor() const
+{
+  if (const auto node = get_min_single_process_node(); node.has_value()) {
+    return node.value()->get_actor();
+  }
+  return std::nullopt;
+}
+
+std::optional<WakeupTreeNode*> WakeupTree::get_min_single_process_node() const
+{
+  if (empty()) {
+    return std::nullopt;
+  }
+  // INVARIANT: The induced post-order relation always places children
+  // in order before the parent. The list of children maintained by
+  // each node represents that ordering, and the first child of
+  // the root is by definition the smallest of the single-process nodes
+  xbt_assert(not this->root_->children_.empty(), "What the");
+  return this->root_->children_.front();
+}
+
+WakeupTree WakeupTree::make_subtree_rooted_at(WakeupTreeNode* root)
+{
+  // Perform a BFS search to perform a deep copy of the portion
+  // of the tree underneath and including `root`. Note that `root`
+  // is contained within the context of a *different* wakeup tree;
+  // hence, we have to be careful to update each node's children
+  // appropriately
+  auto subtree = WakeupTree();
+
+  std::list<std::pair<WakeupTreeNode*, WakeupTreeNode*>> frontier{std::make_pair(root, subtree.root_)};
+  while (not frontier.empty()) {
+    auto [node_in_other_tree, subtree_equivalent] = frontier.front();
+    frontier.pop_front();
+
+    // For each child of the node corresponding to that in `subtree`,
+    // make clones of each of its children and add them to `frontier`
+    // to that their children are added, and so on.
+    for (WakeupTreeNode* child_in_other_tree : node_in_other_tree->get_ordered_children()) {
+      WakeupTreeNode* child_equivalent = subtree.make_node(child_in_other_tree->get_action());
+      subtree_equivalent->add_child(child_equivalent);
+      frontier.push_back(std::make_pair(child_in_other_tree, child_equivalent));
+    }
+  }
+  return subtree;
+}
+
+void WakeupTree::remove_subtree_rooted_at(WakeupTreeNode* root)
+{
+  if (not contains(root)) {
+    throw std::invalid_argument("Attempting to remove a subtree pivoted from a node "
+                                "that is not contained in this wakeup tree");
+  }
+
+  std::list<WakeupTreeNode*> subtree_contents{root};
+  std::list<WakeupTreeNode*> frontier{root};
+  while (not frontier.empty()) {
+    auto node = frontier.front();
+    frontier.pop_front();
+    for (const auto& child : node->get_ordered_children()) {
+      frontier.push_back(child);
+      subtree_contents.push_back(child);
+    }
+  }
+
+  // After having found each node with BFS, now we can
+  // remove them. This prevents the "joys" of iteration during mutation.
+  // We also remove the `root` from being referenced by its own parent (since
+  // it will soon be destroyed)
+  root->detatch_from_parent();
+  for (WakeupTreeNode* node_to_remove : subtree_contents) {
+    this->remove_node(node_to_remove);
+  }
+}
+
+void WakeupTree::remove_min_single_process_subtree()
+{
+  if (const auto node = get_min_single_process_node(); node.has_value()) {
+    remove_subtree_rooted_at(node.value());
+  }
+}
+
+bool WakeupTree::contains(WakeupTreeNode* node) const
+{
+  return std::find_if(this->nodes_.begin(), this->nodes_.end(), [=](const auto& pair) { return pair.first == node; }) !=
+         this->nodes_.end();
+}
+
+WakeupTreeNode* WakeupTree::make_node(std::shared_ptr<Transition> u)
+{
+  auto node                 = std::unique_ptr<WakeupTreeNode>(new WakeupTreeNode(std::move(u)));
+  auto* node_handle         = node.get();
+  this->nodes_[node_handle] = std::move(node);
+  return node_handle;
+}
+
+void WakeupTree::insert_node(std::unique_ptr<WakeupTreeNode> node)
+{
+  auto* node_handle         = node.get();
+  this->nodes_[node_handle] = std::move(node);
+}
+
+void WakeupTree::remove_node(WakeupTreeNode* node)
+{
+  this->nodes_.erase(node);
+}
+
+WakeupTree::InsertionResult WakeupTree::insert(const Execution& E, const PartialExecution& w)
+{
+  // See section 6.2 of Abdulla. et al.'s 2017 ODPOR paper for details
+
+  // Find the first node `v` in the tree such that
+  // `v ~_[E] w` and `v`  is not a leaf node
+  for (WakeupTreeNode* node : *this) {
+    if (const auto shortest_sequence = E.get_shortest_odpor_sq_subset_insertion(node->get_sequence(), w);
+        shortest_sequence.has_value()) {
+      // Insert the sequence as a child of `node`, but only
+      // if the node is not already a leaf
+      if (not node->is_leaf() or node == this->root_) {
+        // NOTE: It's entirely possible that the shortest
+        // sequence we are inserting is empty. Consider the
+        // following two cases:
+        //
+        // 1. `w` is itself empty. Evidently, insertion succeeds but nothing needs
+        // to happen
+        //
+        // 2. a leaf node in the tree already contains `w` exactly.
+        // In this case, the empty `w'` returned (viz. `shortest_seq`)
+        // such that `w [=_[E] v.w'` would be empty
+        this->insert_sequence_after(node, shortest_sequence.value());
+        return node == this->root_ ? InsertionResult::root : InsertionResult::interior_node;
+      }
+      // Since we're following the post-order traversal of the tree,
+      // the first such node we see is the smallest w.r.t "<"
+      return InsertionResult::leaf;
+    }
+  }
+  xbt_die("Insertion should always succeed with the root node (which contains no "
+          "prior execution). If we've reached this point, this implies either that "
+          "the wakeup tree traversal is broken or that computation of the shortest "
+          "sequence to insert into the tree is broken");
+}
+
+void WakeupTree::insert_sequence_after(WakeupTreeNode* node, const PartialExecution& w)
+{
+  WakeupTreeNode* cur_node = node;
+  for (const auto& w_i : w) {
+    WakeupTreeNode* new_node = this->make_node(w_i);
+    cur_node->add_child(new_node);
+    cur_node = new_node;
+  }
+}
+
+} // namespace simgrid::mc::odpor
\ No newline at end of file
diff --git a/src/mc/explo/odpor/WakeupTree.hpp b/src/mc/explo/odpor/WakeupTree.hpp
new file mode 100644 (file)
index 0000000..ea8cf29
--- /dev/null
@@ -0,0 +1,228 @@
+/* Copyright (c) 2007-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. */
+
+#ifndef SIMGRID_MC_ODPOR_WAKEUP_TREE_HPP
+#define SIMGRID_MC_ODPOR_WAKEUP_TREE_HPP
+
+#include "src/mc/explo/odpor/WakeupTreeIterator.hpp"
+#include "src/mc/explo/odpor/odpor_forward.hpp"
+#include "src/mc/transition/Transition.hpp"
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace simgrid::mc::odpor {
+
+/**
+ * @brief A single node in a wakeup tree
+ *
+ * Each node in a wakeup tree contains
+ */
+class WakeupTreeNode {
+private:
+  explicit WakeupTreeNode(std::shared_ptr<Transition> u) : action_(u) {}
+
+  WakeupTreeNode* parent_ = nullptr;
+
+  /** An ordered list of children of for this node in the tree */
+  std::list<WakeupTreeNode*> children_;
+
+  /** @brief The contents of the node */
+  std::shared_ptr<Transition> action_;
+
+  /** @brief Removes the node as a child from the parent */
+  void detatch_from_parent();
+
+  /** Allows the owning tree to insert directly into the child */
+  friend WakeupTree;
+  friend WakeupTreeIterator;
+
+public:
+  ~WakeupTreeNode()                                = default;
+  WakeupTreeNode(const WakeupTreeNode&)            = delete;
+  WakeupTreeNode(WakeupTreeNode&&)                 = default;
+  WakeupTreeNode& operator=(const WakeupTreeNode&) = delete;
+  WakeupTreeNode& operator=(WakeupTreeNode&&)      = default;
+
+  auto begin() const { return this->children_.begin(); }
+  auto end() const { return this->children_.end(); }
+  auto rbegin() const { return this->children_.rbegin(); }
+  auto rend() const { return this->children_.rend(); }
+
+  bool is_leaf() const { return children_.empty(); }
+  bool is_root() const { return parent_ == nullptr; }
+  aid_t get_actor() const { return action_->aid_; }
+  PartialExecution get_sequence() const;
+  std::shared_ptr<Transition> get_action() const { return action_; }
+  const std::list<WakeupTreeNode*>& get_ordered_children() const { return children_; }
+
+  /** Insert a node `node` as a new child of this node */
+  void add_child(WakeupTreeNode* node);
+};
+
+/**
+ * @brief The structure used by ODPOR to maintains paths of execution
+ * that should be followed in the future
+ *
+ * The wakeup tree data structure is formally defined in the Abdulla et al.
+ * 2017 ODPOR paper. Conceptually, the tree consists of nodes which are
+ * mapped to actions. Each node represents a partial extension of an execution,
+ * the complete extension being the transitions taken in sequence from
+ * the root of the tree to the node itself. Leaf nodes in the tree conceptually,
+ * then, represent paths that are guaranteed to explore different parts
+ * of the search space.
+ *
+ * Iteration over a wakeup tree occurs as a post-order traversal of its nodes
+ *
+ * @note A wakeup tree is defined relative to some execution `E`. The
+ * structure itself does not hold onto a reference of the execution with
+ * respect to which it is a wakeup tree.
+ *
+ * @todo: If the idea of execution "views"  is ever added -- viz. being able
+ * to share the contents of a single execution -- then a wakeup tree could
+ * contain a reference to such a view which would then be maintained by the
+ * manipulator of the tree
+ */
+class WakeupTree {
+private:
+  WakeupTreeNode* root_;
+
+  /**
+   * @brief All of the nodes that are currently are a part of the tree
+   *
+   * @invariant Each node event maps itself to the owner of that node,
+   * i.e. the unique pointer that manages the data at the address. The tree owns all
+   * of the addresses that are referenced by the nodes WakeupTreeNode.
+   * ODPOR guarantees that nodes are persisted as long as needed.
+   */
+  std::unordered_map<WakeupTreeNode*, std::unique_ptr<WakeupTreeNode>> nodes_;
+
+  void insert_node(std::unique_ptr<WakeupTreeNode> node);
+  void insert_sequence_after(WakeupTreeNode* node, const PartialExecution& w);
+  void remove_node(WakeupTreeNode* node);
+  bool contains(WakeupTreeNode* node) const;
+
+  /**
+   * @brief Removes the node `root` and all of its descendants from
+   * this wakeup tree
+   *
+   * @throws: If the node `root` is not contained in this tree, an
+   * exception is raised
+   */
+  void remove_subtree_rooted_at(WakeupTreeNode* root);
+
+  /**
+   * @brief Adds a new node to the tree, disconnected from
+   * any other, which represents the partial execution
+   * "fragment" `u`
+   */
+  WakeupTreeNode* make_node(std::shared_ptr<Transition> u);
+
+  /* Allow the iterator to access the contents of the tree */
+  friend WakeupTreeIterator;
+
+public:
+  WakeupTree();
+  explicit WakeupTree(std::unique_ptr<WakeupTreeNode> root);
+
+  /**
+   * @brief Creates a copy of the subtree whose root is the node
+   * `root` in this tree
+   */
+  static WakeupTree make_subtree_rooted_at(WakeupTreeNode* root);
+
+  auto begin() const { return WakeupTreeIterator(*this); }
+  auto end() const { return WakeupTreeIterator(); }
+
+  std::vector<std::string> get_single_process_texts() const;
+
+  /**
+   * @brief Remove the subtree of the smallest (with respect
+   * to the tree's "<" relation) single-process node.
+   *
+   * A "single-process" node is one whose execution represents
+   * taking a single action (i.e. those of the root node). The
+   * smallest under "<" is that which is continuously selected and
+   * removed by ODPOR.
+   *
+   * If the tree is empty, this method has no effect.
+   */
+  void remove_min_single_process_subtree();
+
+  /**
+   * @brief Whether or not this tree is considered empty
+   *
+   * @note Unlike other collection types, a wakeup tree is
+   * considered "empty" if it only contains the root node;
+   * that is, if it is "uninteresting". In such a case,
+   */
+  bool empty() const { return nodes_.size() == static_cast<size_t>(1); }
+
+  /**
+   * @brief Returns the number of *non-empty* entries in the tree, viz. the
+   * number of nodes in the tree that have an action mapped to them
+   */
+  size_t get_num_entries() const { return !empty() ? (nodes_.size() - 1) : static_cast<size_t>(0); }
+
+  /**
+   * @brief Returns the number of nodes in the tree, including the root node
+   */
+  size_t get_num_nodes() const { return nodes_.size(); }
+
+  /**
+   * @brief Gets the actor of the node that is the "smallest" (with respect
+   * to the tree's "<" relation) single-process node.
+   *
+   * If the tree is empty, returns std::nullopt
+   */
+  std::optional<aid_t> get_min_single_process_actor() const;
+
+  /**
+   * @brief Gets the node itself that is the "smallest" (with respect
+   * to the tree's "<" relation) single-process node.
+   *
+   * If the tree is empty, returns std::nullopt
+   */
+  std::optional<WakeupTreeNode*> get_min_single_process_node() const;
+
+  /** @brief Describes how a tree insertion was carried out */
+  enum class InsertionResult { leaf, interior_node, root };
+
+  /**
+   * @brief Inserts an sequence `seq` of processes into the tree
+   * such that that this tree is a wakeup tree relative to the
+   * given execution
+   *
+   * A key component of managing wakeup trees in ODPOR is
+   * determining what should be inserted into a wakeup tree.
+   * The procedure for implementing the insertion is outlined in section 6.2
+   * of Abdulla et al. 2017 as follows:
+   *
+   * | Let `v` be the smallest (w.r.t to "<") sequence in [the tree] B
+   * | such that `v ~_[E] w`. If `v` is a leaf node, the tree can be left
+   * | unmodified.
+   * |
+   * | Otherwise let `w'` be the shortest sequence such that `w [=_[E] v.w'`
+   * | and add `v.w'` as a new leaf, ordered after all already existing nodes
+   * | of the form `v.w''`
+   *
+   * This method performs the post-order search of part one and the insertion of
+   * `v.w'` of part two of the above procedure. Note that the execution will
+   * provide `v.w'` (see `Execution::get_shortest_odpor_sq_subset_insertion()`).
+   *
+   * @invariant: It is assumed that this tree is a wakeup tree
+   * with respect to the given execution `E`
+   *
+   * @return Whether a sequence equivalent to `seq` is already contained
+   * as a leaf node in the tree
+   */
+  InsertionResult insert(const Execution& E, const PartialExecution& seq);
+};
+
+} // namespace simgrid::mc::odpor
+#endif
diff --git a/src/mc/explo/odpor/WakeupTreeIterator.cpp b/src/mc/explo/odpor/WakeupTreeIterator.cpp
new file mode 100644 (file)
index 0000000..6c81203
--- /dev/null
@@ -0,0 +1,73 @@
+/* Copyright (c) 2008-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. */
+
+#include "src/mc/explo/odpor/WakeupTreeIterator.hpp"
+#include "src/mc/explo/odpor/WakeupTree.hpp"
+
+namespace simgrid::mc::odpor {
+
+WakeupTreeIterator::WakeupTreeIterator(const WakeupTree& tree) : root_list{tree.root_}
+{
+  post_order_iteration.push(root_list.begin());
+  push_until_left_most_found();
+}
+
+void WakeupTreeIterator::push_until_left_most_found()
+{
+  // INVARIANT: Since we are traversing over a tree,
+  // there are no cycles. This means that at least
+  // one node in the tree won't have any children,
+  // so the loop will eventually terminate
+  auto* cur_top_node = *post_order_iteration.top();
+  while (not cur_top_node->is_leaf()) {
+    // INVARIANT: Since we push children in
+    // reverse order (right-most to left-most),
+    // we ensure that we'll always process left-most
+    // children first
+    auto& children = cur_top_node->children_;
+
+    for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
+      // iter.base() points one element past where we seek; hence,
+      // we move it over one position
+      post_order_iteration.push(std::prev(iter.base()));
+    }
+    cur_top_node = *post_order_iteration.top();
+  }
+}
+
+void WakeupTreeIterator::increment()
+{
+  // If there are no nodes in the stack, we've
+  // completed the traversal: there's nothing left
+  // to do
+  if (post_order_iteration.empty()) {
+    return;
+  }
+
+  auto prev_top_handle = post_order_iteration.top();
+  post_order_iteration.pop();
+
+  // If there are now no longer any nodes left,
+  // we know that `prev_top` must be the original
+  // root; that is, we were *just* pointing at the
+  // original root, so we're done
+  if (post_order_iteration.empty()) {
+    return;
+  }
+
+  // Otherwise, look at the next top node. If
+  // `prev_top` is that node's right-most child,
+  // then we don't attempt to re-add `next_top`'s
+  // children again for we would have already seen them.
+  // To actually determine "right-most", we check if
+  // moving over to the right one spot brings us to the
+  // end of the candidate parent's list
+  const auto* next_top_node = *post_order_iteration.top();
+  if ((++prev_top_handle) != next_top_node->get_ordered_children().end()) {
+    push_until_left_most_found();
+  }
+}
+
+} // namespace simgrid::mc::odpor
\ No newline at end of file
diff --git a/src/mc/explo/odpor/WakeupTreeIterator.hpp b/src/mc/explo/odpor/WakeupTreeIterator.hpp
new file mode 100644 (file)
index 0000000..32b968c
--- /dev/null
@@ -0,0 +1,71 @@
+/* Copyright (c) 2007-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. */
+
+#ifndef SIMGRID_MC_ODPOR_WAKEUP_TREE_ITERATOR_HPP
+#define SIMGRID_MC_ODPOR_WAKEUP_TREE_ITERATOR_HPP
+
+#include "src/mc/explo/odpor/odpor_forward.hpp"
+
+#include <boost/iterator/iterator_facade.hpp>
+#include <list>
+#include <stack>
+
+namespace simgrid::mc::odpor {
+
+/**
+ * @brief A forward-iterator that performs a postorder traversal
+ * of the nodes of a WakeupTree
+ *
+ * Inserting a sequence `w` into a wakeup tree `B` with respect to
+ * some execution `E` requires determining the "<-minimal" node `N`
+ * with sequence `v` in the tree such that `v ~_[E] w`. The "<" relation
+ * over a wakeup tree orders its nodes by first recursively ordering all
+ * children of a node `N` followed by the node `N` itself, viz. a postorder.
+ * This iterator provides such a postorder traversal over the nodes in the
+ * wakeup tree.
+ */
+struct WakeupTreeIterator
+    : public boost::iterator_facade<WakeupTreeIterator, WakeupTreeNode*, boost::forward_traversal_tag> {
+public:
+  WakeupTreeIterator() = default;
+  explicit WakeupTreeIterator(const WakeupTree& tree);
+
+private:
+  using node_handle = std::list<WakeupTreeNode*>::iterator;
+
+  /**
+   *  @brief A list which is used to "store" the root node of the traversed
+   * wakeup tree
+   *
+   * The root node is, by definition, not the child of any other node. This
+   * means that the root node also is contained in any list into which the
+   * iterator can generate a pointer (iterator). This list takes the role
+   * of allowing the iterator to treat the root node like any other.
+   */
+  std::list<WakeupTreeNode*> root_list;
+
+  /**
+   * @brief The current "view" of the iteration in post-order traversal
+   */
+  std::stack<node_handle> post_order_iteration;
+
+  /**
+   * @brief Search the wakeup tree until a leaf node appears at the front
+   * of the iteration, pushing all children towards the top of the stack
+   * as the search progresses
+   */
+  void push_until_left_most_found();
+
+  // boost::iterator_facade<...> interface to implement
+  void increment();
+  bool equal(const WakeupTreeIterator& other) const { return post_order_iteration == other.post_order_iteration; }
+  WakeupTreeNode*& dereference() const { return *post_order_iteration.top(); }
+
+  // Allows boost::iterator_facade<...> to function properly
+  friend class boost::iterator_core_access;
+};
+
+} // namespace simgrid::mc::odpor
+#endif
diff --git a/src/mc/explo/odpor/WakeupTree_test.cpp b/src/mc/explo/odpor/WakeupTree_test.cpp
new file mode 100644 (file)
index 0000000..e0ef032
--- /dev/null
@@ -0,0 +1,469 @@
+/* Copyright (c) 2017-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. */
+
+#include "src/3rd-party/catch.hpp"
+#include "src/mc/explo/odpor/Execution.hpp"
+#include "src/mc/explo/odpor/WakeupTree.hpp"
+#include "src/mc/explo/udpor/udpor_tests_private.hpp"
+#include "src/xbt/utils/iter/LazyPowerset.hpp"
+
+using namespace simgrid::mc;
+using namespace simgrid::mc::odpor;
+using namespace simgrid::mc::udpor;
+
+static void test_tree_iterator(const WakeupTree& tree, const std::vector<PartialExecution>& expected)
+{
+  uint64_t num_nodes_traversed = 0;
+  auto tree_iter               = tree.begin();
+  for (auto expected_iter = expected.begin(); expected_iter != expected.end();
+       ++expected_iter, ++tree_iter, ++num_nodes_traversed) {
+    REQUIRE(tree_iter != tree.end());
+    REQUIRE((*tree_iter)->get_sequence() == *expected_iter);
+  }
+  REQUIRE(num_nodes_traversed == tree.get_num_nodes());
+}
+
+static void test_tree_empty(const WakeupTree& tree)
+{
+  REQUIRE(tree.empty());
+  REQUIRE(tree.get_num_entries() == 0);
+  REQUIRE(tree.get_num_nodes() == 1);
+  REQUIRE_FALSE(tree.get_min_single_process_node().has_value());
+  REQUIRE_FALSE(tree.get_min_single_process_actor().has_value());
+  test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{}});
+}
+
+TEST_CASE("simgrid::mc::odpor::WakeupTree: Constructing Trees")
+{
+  SECTION("Constructing empty trees")
+  {
+    test_tree_empty(WakeupTree());
+  }
+
+  SECTION("Testing subtree creation and manipulation")
+  {
+    // Here, we make everything dependent. This will ensure that each unique sequence
+    // inserted into the tree never "eventually looks like"
+    const auto a0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a1 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto a2 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto a3 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 4);
+    const auto a4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 5);
+    const auto a5 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 6);
+
+    Execution execution;
+    execution.push_transition(a0);
+    execution.push_transition(a1);
+    execution.push_transition(a2);
+    execution.push_transition(a3);
+    execution.push_transition(a4);
+    execution.push_transition(a5);
+
+    // The tree is as follows:
+    //                  {}
+    //               /     /
+    //             a1       a4
+    //           /    /       /
+    //          a2    a3       a1
+    //         /     /   /      /
+    //        a3    a2   a5     a2
+    //       /     /             /
+    //      a4    a4             a3
+    //
+    // Recall that new nodes (in this case the one with
+    // action `a2`) are added such that they are "greater than" (under
+    // the tree's `<` relation) all those that exist under the given parent
+    WakeupTree tree;
+    tree.insert(Execution(), {a1, a2, a3, a4});
+    tree.insert(Execution(), {a1, a3, a2, a4});
+    tree.insert(Execution(), {a1, a3, a2, a4, a5});
+    tree.insert(Execution(), {a1, a3, a5});
+    tree.insert(Execution(), {a4, a2, a1, a3});
+    REQUIRE(tree.get_num_nodes() == 13);
+    test_tree_iterator(tree, std::vector<PartialExecution>{
+                                 PartialExecution{a1, a2, a3, a4}, PartialExecution{a1, a2, a3},
+                                 PartialExecution{a1, a2}, PartialExecution{a1, a3, a2, a4},
+                                 PartialExecution{a1, a3, a2}, PartialExecution{a1, a3, a5}, PartialExecution{a1, a3},
+                                 PartialExecution{a1}, PartialExecution{a4, a2, a1, a3}, PartialExecution{a4, a2, a1},
+                                 PartialExecution{a4, a2}, PartialExecution{a4}, PartialExecution{}});
+
+    SECTION("Cloning a tree from the root produces the same tree")
+    {
+      // The root node is the last node
+      auto tree_root = tree.begin();
+      std::advance(tree_root, tree.get_num_nodes() - 1);
+
+      WakeupTree clone = WakeupTree::make_subtree_rooted_at(*tree_root);
+      REQUIRE(clone.empty() == tree.empty());
+      REQUIRE(clone.get_num_entries() == tree.get_num_entries());
+      REQUIRE(clone.get_num_nodes() == tree.get_num_nodes());
+
+      auto tree_iter = tree.begin();
+      for (auto clone_iter = clone.begin(); clone_iter != clone.end(); ++clone_iter, ++tree_iter) {
+        REQUIRE(tree_iter != tree.end());
+        REQUIRE((*tree_iter)->get_sequence() == (*clone_iter)->get_sequence());
+      }
+    }
+
+    SECTION("Cloning a subtree from a leaf gives an empty tree")
+    {
+      // Let's pick the first leaf
+      WakeupTree clone = WakeupTree::make_subtree_rooted_at(*tree.begin());
+      REQUIRE(clone.empty());
+      REQUIRE(clone.get_num_entries() == 0);
+      REQUIRE(clone.get_num_nodes() == 1);
+    }
+
+    SECTION("Cloning a subtree from an interior node gives the subtree underneath")
+    {
+      // Here, we pick the second-to-last node in the
+      // series, which is the right-most child of the root
+      auto right_most = tree.begin();
+      std::advance(right_most, tree.get_num_nodes() - 2);
+
+      WakeupTree clone = WakeupTree::make_subtree_rooted_at(*right_most);
+      REQUIRE_FALSE(clone.empty());
+      REQUIRE(clone.get_num_entries() == 3);
+      REQUIRE(clone.get_num_nodes() == 4);
+      // Importantly, note that action `a4` is not included in
+      // any of the executions; for in the subtree `clone` `a4`
+      // is now the root.
+      test_tree_iterator(clone, std::vector<PartialExecution>{PartialExecution{a2, a1, a3}, PartialExecution{a2, a1},
+                                                              PartialExecution{a2}, PartialExecution{}});
+    }
+
+    SECTION("Removing the first single-process subtree")
+    {
+      // Prior to removal, the first `a1` was the first single-process node
+      REQUIRE(tree.get_min_single_process_node().has_value());
+      REQUIRE(tree.get_min_single_process_actor().has_value());
+      REQUIRE(tree.get_min_single_process_actor().value() == a1->aid_);
+
+      tree.remove_min_single_process_subtree();
+
+      // Now the first `a4` is
+      REQUIRE(tree.get_min_single_process_node().has_value());
+      REQUIRE(tree.get_min_single_process_actor().has_value());
+      REQUIRE(tree.get_min_single_process_actor().value() == a4->aid_);
+
+      REQUIRE(tree.get_num_nodes() == 5);
+      test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a4, a2, a1, a3},
+                                                             PartialExecution{a4, a2, a1}, PartialExecution{a4, a2},
+                                                             PartialExecution{a4}, PartialExecution{}});
+      tree.remove_min_single_process_subtree();
+
+      // At this point, we've removed each single-process subtree, so
+      // the tree should be empty
+      REQUIRE(tree.empty());
+    }
+
+    SECTION("Removing the first single-process subtree from an empty tree has no effect")
+    {
+      WakeupTree empty_tree;
+      test_tree_empty(empty_tree);
+
+      empty_tree.remove_min_single_process_subtree();
+
+      // There should be no effect: the tree should still be empty
+      // and the function should have no effect
+      test_tree_empty(empty_tree);
+    }
+  }
+}
+
+TEST_CASE("simgrid::mc::odpor::WakeupTree: Testing Insertion for Empty Executions")
+{
+  SECTION("Following an execution")
+  {
+    // We imagine the following completed execution `E`
+    // consisting of executing actions a0-a3. Execution
+    // `E` is the first such maximal execution explored
+    // by ODPOR, which implies that a) all sleep sets are
+    // empty and b) all wakeup trees (i.e. for each prefix) consist of the root
+    // node with a single leaf containing the action
+    // taken, save for the wakeup tree of the execution itself
+    // which is empty.
+
+    // We first notice that there's a reversible race between
+    // events 0 and 3.
+
+    const auto a0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto a1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
+    const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a3 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+    const auto a4 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
+
+    Execution execution;
+    execution.push_transition(a0);
+    execution.push_transition(a1);
+    execution.push_transition(a2);
+    execution.push_transition(a3);
+    execution.push_transition(a4);
+
+    REQUIRE(execution.get_racing_events_of(2) == std::unordered_set<Execution::EventHandle>{0});
+    REQUIRE(execution.get_racing_events_of(3) == std::unordered_set<Execution::EventHandle>{0});
+
+    WakeupTree tree;
+
+    SECTION("Attempting to insert the empty sequence into an empty tree should have no effect")
+    {
+      tree.insert(Execution(), {});
+      test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{}});
+    }
+
+    // First, we initialize the tree to how it looked prior
+    // to the insertion of the race.
+    tree.insert(Execution(), {a0});
+
+    // Then, after insertion, we ensure that the node was
+    // indeed added to the tree.
+    tree.insert(Execution(), {a1, a3});
+    test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a1, a3},
+                                                           PartialExecution{a1}, PartialExecution{}});
+
+    SECTION("Attempting to re-insert the same EXACT sequence should have no effect")
+    {
+      tree.insert(Execution(), {a1, a3});
+      test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a1, a3},
+                                                             PartialExecution{a1}, PartialExecution{}});
+    }
+
+    SECTION("Attempting to re-insert an equivalent sequence should have no effect")
+    {
+      // a3 and a1 are interchangeable since `a1` is independent with everything.
+      // Since we found an equivalent sequence that is a leaf, nothing should result..
+      tree.insert(Execution(), {a3, a1});
+      test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a1, a3},
+                                                             PartialExecution{a1}, PartialExecution{}});
+    }
+
+    SECTION("Attempting to insert the empty sequence should have no effect")
+    {
+      tree.insert(Execution(), {});
+      test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a1, a3},
+                                                             PartialExecution{a1}, PartialExecution{}});
+    }
+
+    SECTION("Inserting an extension should create a branch point")
+    {
+      // `a1.a2` shares the same `a1` prefix as `a1.a3`. Thus, the tree
+      // should now look as follows:
+      //
+      //                 {}
+      //               /    /
+      //             a0     a1
+      //                   /   /
+      //                  a3   a4
+      //
+      // Recall that new nodes (in this case the one with
+      // action `a2`) are added such that they are "greater than" (under
+      // the tree's `<` relation) all those that exist under the given parent.
+      tree.insert(Execution(), {a1, a4});
+      test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a1, a3},
+                                                             PartialExecution{a1, a4}, PartialExecution{a1},
+                                                             PartialExecution{}});
+    }
+
+    SECTION("Inserting an equivalent sequence to a leaf should preserve the tree as-is")
+    {
+      // `a1.a2` is equivalent to `a1.a3` since `a2` and `a3` are independent
+      // (`E ⊢ p ◊ w` where `p := proc(a2)` and `w := a3`). Thus, the tree
+      // should now STILL look as follows:
+      //
+      //                 {}
+      //               /    /
+      //             a0     a1
+      //                   /
+      //                  a3
+      //
+      // Recall that new nodes (in this case the one with
+      // action `a2`) are added such that they are "greater than" (under
+      // the tree's `<` relation) all those that exist under the given parent.
+      tree.insert(Execution(), {a1, a3});
+      test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a1, a3},
+                                                             PartialExecution{a1}, PartialExecution{}});
+    }
+  }
+
+  SECTION("Performing Arbitrary Insertions")
+  {
+    const auto a0 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto a1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 4);
+    const auto a2 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto a3 = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto a4 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto a5 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 4);
+    WakeupTree tree;
+
+    SECTION("Attempting to insert the empty sequence into an empty tree should have no effect")
+    {
+      tree.insert(Execution(), {});
+      test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{}});
+    }
+
+    SECTION("Attempting to re-insert the same sequence multiple times should have no extra effect")
+    {
+      tree.insert(Execution(), {a4});
+      tree.insert(Execution(), {a4});
+      tree.insert(Execution(), {a4});
+      REQUIRE(tree.get_num_nodes() == 2);
+      test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a4}, PartialExecution{}});
+    }
+
+    SECTION("Attempting to insert an independent sequence same should have no extra effect")
+    {
+      // a4 and a1 are independent actions. Intuitively, then, we need only
+      // search one ordering of the two actions. The wakeup tree handles
+      // this by computing the `~` relation. The relation itself determines
+      // whether the `a1` is an initial of `a3`, which it is not. It then
+      // checks whether `a1` is independent with everything in the sequence
+      // (in this case, consisting only of `a1`) which IS true. Since `a4`
+      // is already a leaf node of the tree, it suffices to only have `a4`
+      // in this tree to guide ODPOR.
+      tree.insert(Execution(), {a4});
+      tree.insert(Execution(), {a1});
+      REQUIRE(tree.get_num_nodes() == 2);
+      test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a4}, PartialExecution{}});
+    }
+
+    SECTION(
+        "Attempting to insert a progression of executions should have no extra effect when the first process is a leaf")
+    {
+      // All progressions starting with `a0` are effectively already accounted
+      // for by inserting `a0` since we `a0` "can always be made to look like"
+      // (viz. the `~` relation) `a0.*` where `*` is some sequence of actions
+      tree.insert(Execution(), {a0});
+      tree.insert(Execution(), {a0, a3});
+      tree.insert(Execution(), {a0, a3, a2});
+      tree.insert(Execution(), {a0, a3, a2, a4});
+      tree.insert(Execution(), {a0, a3, a2, a4});
+      REQUIRE(tree.get_num_nodes() == 2);
+      test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{}});
+    }
+
+    SECTION("Stress test with multiple branch points: `~_E` with different looking sequences")
+    {
+      // After the insertions below, the tree looks like the following:
+      //                {}
+      //              /    /
+      //            a0     a2
+      //                 /  |   /
+      //               a0  a3   a5
+      tree.insert(Execution(), {a0});
+      tree.insert(Execution(), {a2, a0});
+      tree.insert(Execution(), {a2, a3});
+      tree.insert(Execution(), {a2, a5});
+      REQUIRE(tree.get_num_nodes() == 6);
+      test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a2, a0},
+                                                             PartialExecution{a2, a3}, PartialExecution{a2, a5},
+                                                             PartialExecution{a2}, PartialExecution{}});
+      SECTION("Adding more stress")
+      {
+        // In this case, `a2` and `a1` can be interchanged with each other.
+        // Thus `a2.a1 == a1.a2`. Since there is already an interior node
+        // containing `a2`, we attempt to add the what remains (viz. `a1`) to the
+        // series. HOWEVER: we notice that `a2.a5` is "eventually equivalent to"
+        // (that is `~` with) `a1.a2` since `a2` is an initial of the latter and
+        // `a1` and `a5` are independent of each other.
+        tree.insert(Execution(), {a1, a2});
+        REQUIRE(tree.get_num_nodes() == 6);
+        test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a2, a0},
+                                                               PartialExecution{a2, a3}, PartialExecution{a2, a5},
+                                                               PartialExecution{a2}, PartialExecution{}});
+
+        // With a3.a0, we notice that starting a sequence with `a3` is
+        // always different than starting one with either `a0` or
+        //
+        // After the insertion, the tree looks like the following:
+        //                     {}
+        //              /     /        /
+        //            a0     a2        a3
+        //                 /  |  /     |
+        //               a0  a3  a5    a0
+        tree.insert(Execution(), {a3, a0});
+        REQUIRE(tree.get_num_nodes() == 8);
+        test_tree_iterator(tree, std::vector<PartialExecution>{PartialExecution{a0}, PartialExecution{a2, a0},
+                                                               PartialExecution{a2, a3}, PartialExecution{a2, a5},
+                                                               PartialExecution{a2}, PartialExecution{a3, a0},
+                                                               PartialExecution{a3}, PartialExecution{}});
+      }
+    }
+  }
+
+  SECTION("Insertion with more subtle equivalents")
+  {
+    const auto cd_1 = std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto i_2  = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+    const auto i_3  = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
+    const auto d_1  = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1);
+    const auto d_2  = std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 2);
+    WakeupTree complex_tree;
+    // After the insertions below, the tree looks like the following:
+    //                              {}
+    //                           /     /
+    //                       cd_1      i_2
+    //                       /            /
+    //                    i_2              d_2
+    //                   /                  /
+    //                 d_1                 cd_1
+    //                 /                 /      /
+    //               i_3               d_1      i_3
+    //              /                  /          /
+    //            d_2                i_3          d_2
+    //            /                  /              /
+    //          d_2                d_2              d_1
+    //
+    // d_1.i_3.d_2 is equivalent to i_3.d_2.d_1
+    complex_tree.insert(Execution(), {cd_1, i_2, d_1, i_3, d_2, d_2});
+    complex_tree.insert(Execution(), {i_2, d_2, cd_1, d_1, i_3, d_2});
+    complex_tree.insert(Execution(), {i_2, d_2, cd_1, i_3, d_2, d_1});
+    REQUIRE(complex_tree.get_num_nodes() == 16);
+    test_tree_iterator(complex_tree, std::vector<PartialExecution>{{cd_1, i_2, d_1, i_3, d_2, d_2},
+                                                                   {cd_1, i_2, d_1, i_3, d_2},
+                                                                   {cd_1, i_2, d_1, i_3},
+                                                                   {cd_1, i_2, d_1},
+                                                                   {cd_1, i_2},
+                                                                   {cd_1},
+                                                                   {i_2, d_2, cd_1, d_1, i_3, d_2},
+                                                                   {i_2, d_2, cd_1, d_1, i_3},
+                                                                   {i_2, d_2, cd_1, d_1},
+                                                                   {i_2, d_2, cd_1, i_3, d_2, d_1},
+                                                                   {i_2, d_2, cd_1, i_3, d_2},
+                                                                   {i_2, d_2, cd_1, i_3},
+                                                                   {i_2, d_2, cd_1},
+                                                                   {i_2, d_2},
+                                                                   {i_2},
+                                                                   {}});
+    // Here we note that the sequence that we are attempting to insert, viz.
+    //
+    //    i_3.i_2.d_2.cd_1.d_2.d_1
+    //
+    // is already equivalent to
+    //
+    //    i_2.d_2.cd_1.i_3.d_2.d_1
+    complex_tree.insert(Execution(), {i_3, i_2, d_2, cd_1, d_2, d_1});
+    REQUIRE(complex_tree.get_num_nodes() == 16);
+
+    // Here we note that the sequence that we are attempting to insert, viz.
+    //
+    //    i_2.d_2.cd_1.d_1.i_3
+    //
+    // is already equivalent to
+    //
+    //    i_2.d_2.cd_1.i_3.d_2.d_1
+    complex_tree.insert(Execution(), {i_2, d_2, cd_1, d_1, i_3});
+    REQUIRE(complex_tree.get_num_nodes() == 16);
+
+    // Here we note that the sequence that we are attempting to insert, viz.
+    //
+    //    i_2.d_2.cd_1
+    //
+    // is accounted for by an interior node of the tree. Since there is no
+    // "extra" portions that are different from what is already
+    // contained in the tree, nothing is added and the tree stays the same
+    complex_tree.insert(Execution(), {i_2, d_2, cd_1});
+    REQUIRE(complex_tree.get_num_nodes() == 16);
+  }
+}
\ No newline at end of file
diff --git a/src/mc/explo/odpor/odpor_forward.hpp b/src/mc/explo/odpor/odpor_forward.hpp
new file mode 100644 (file)
index 0000000..52388bc
--- /dev/null
@@ -0,0 +1,43 @@
+/* Copyright (c) 2007-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. */
+
+/** @file odpor_forward.hpp
+ *
+ *  Forward definitions for MC types specific to ODPOR
+ */
+
+#ifndef SIMGRID_MC_ODPOR_FORWARD_HPP
+#define SIMGRID_MC_ODPOR_FORWARD_HPP
+
+#include "src/mc/mc_forward.hpp"
+#include <list>
+#include <memory>
+#include <simgrid/forward.h>
+
+namespace simgrid::mc::odpor {
+
+using PartialExecution = std::list<std::shared_ptr<Transition>>;
+
+class Event;
+class Execution;
+class ReversibleRaceCalculator;
+class WakeupTree;
+class WakeupTreeNode;
+class WakeupTreeIterator;
+
+} // namespace simgrid::mc::odpor
+
+namespace simgrid::mc {
+
+// Permit ODPOR or SDPOR to be used as namespaces
+// Many of the structures overlap, so it doesn't
+// make sense to some in one and not the other.
+// Having one for each algorithm makes the corresponding
+// code easier to read
+namespace sdpor = simgrid::mc::odpor;
+
+} // namespace simgrid::mc
+
+#endif
diff --git a/src/mc/explo/odpor/odpor_tests_private.hpp b/src/mc/explo/odpor/odpor_tests_private.hpp
new file mode 100644 (file)
index 0000000..0a29f7d
--- /dev/null
@@ -0,0 +1,52 @@
+/* Copyright (c) 2007-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. */
+
+/** @file odpor_tests_private.hpp
+ *
+ * A private header file for all ODPOR tests
+ */
+
+#ifndef SIMGRID_MC_ODPOR_TEST_PRIVATE_HPP
+#define SIMGRID_MC_ODPOR_TEST_PRIVATE_HPP
+
+#include "src/mc/explo/udpor/udpor_tests_private.hpp"
+#include "src/mc/transition/Transition.hpp"
+
+namespace simgrid::mc::odpor {
+
+struct DependentIfSameValueAction : public Transition {
+private:
+  const int value;
+
+public:
+  DependentIfSameValueAction() = default;
+  DependentIfSameValueAction(Type type, aid_t issuer, int value, int times_considered = 0)
+      : Transition(type, issuer, times_considered), value(value)
+  {
+  }
+  DependentIfSameValueAction(aid_t issuer, int value, int times_considered = 0)
+      : Transition(simgrid::mc::Transition::Type::UNKNOWN, issuer, times_considered), value(value)
+  {
+  }
+
+  // Dependent only with DependentAction (i.e. not itself)
+  bool depends(const Transition* other) const override
+  {
+    if (aid_ == other->aid_) {
+      return true;
+    }
+
+    if (const auto* same_value = dynamic_cast<const DependentIfSameValueAction*>(other); same_value != nullptr) {
+      return value == same_value->value;
+    }
+
+    // `DependentAction` is dependent with everyone who's not the `IndependentAction`
+    return dynamic_cast<const simgrid::mc::udpor::DependentAction*>(other) != nullptr;
+  }
+};
+
+} // namespace simgrid::mc::odpor
+
+#endif
index 4e51cdd..513c997 100644 (file)
@@ -37,14 +37,15 @@ int main(int argc, char** argv)
 
 #if SIMGRID_HAVE_STATEFUL_MC
   if (_sg_mc_comms_determinism || _sg_mc_send_determinism)
-    explo = std::unique_ptr<Exploration>(create_communication_determinism_checker(argv_copy, cfg_use_DPOR()));
+    explo = std::unique_ptr<Exploration>(
+        create_communication_determinism_checker(argv_copy, get_model_checking_reduction()));
   else if (_sg_mc_unfolding_checker)
     explo = std::unique_ptr<Exploration>(create_udpor_checker(argv_copy));
   else if (not _sg_mc_property_file.get().empty())
     explo = std::unique_ptr<Exploration>(create_liveness_checker(argv_copy));
   else
 #endif
-    explo = std::unique_ptr<Exploration>(create_dfs_exploration(argv_copy, cfg_use_DPOR()));
+    explo = std::unique_ptr<Exploration>(create_dfs_exploration(argv_copy, get_model_checking_reduction()));
 
   ExitStatus status;
   try {
index 85c7664..4e33378 100644 (file)
@@ -466,7 +466,8 @@ TEST_CASE("simgrid::mc::udpor::maximal_subsets_iterator: Basic Testing of Maxima
     {
       EventSet interesting_bunch{&e2, &e4, &e7, &e8};
 
-      maximal_subsets_iterator first(C, [&](const UnfoldingEvent* e) { return interesting_bunch.contains(e); });
+      maximal_subsets_iterator first(
+          C, [&interesting_bunch](const UnfoldingEvent* e) { return interesting_bunch.contains(e); });
       maximal_subsets_iterator last;
 
       for (; first != last; ++first) {
@@ -499,7 +500,8 @@ TEST_CASE("simgrid::mc::udpor::maximal_subsets_iterator: Basic Testing of Maxima
     {
       EventSet interesting_bunch{&e3, &e5, &e6};
 
-      maximal_subsets_iterator first(C, [&](const UnfoldingEvent* e) { return interesting_bunch.contains(e); });
+      maximal_subsets_iterator first(
+          C, [&interesting_bunch](const UnfoldingEvent* e) { return interesting_bunch.contains(e); });
       maximal_subsets_iterator last;
 
       for (; first != last; ++first) {
@@ -1364,4 +1366,4 @@ TEST_CASE("simgrid::mc::udpor::Configuration: Computing Full Alternatives in Rea
       REQUIRE(alternative.value().get_events() == EventSet({e0_handle, e7_handle, e9_handle}));
     }
   }
-}
\ No newline at end of file
+}
index 6ad0c6c..571ec6e 100644 (file)
@@ -57,8 +57,9 @@ int _sg_mc_max_visited_states = 0;
 static simgrid::config::Flag<std::string> cfg_mc_reduction{
     "model-check/reduction", "Specify the kind of exploration reduction (either none or DPOR)", "dpor",
     [](std::string_view value) {
-      if (value != "none" && value != "dpor")
-        xbt_die("configuration option 'model-check/reduction' can only take 'none' or 'dpor' as a value");
+      if (value != "none" && value != "dpor" && value != "sdpor" && value != "odpor")
+        xbt_die("configuration option 'model-check/reduction' must be one of the following: "
+                " 'none', 'dpor', 'sdpor', or 'odpor'");
     }};
 
 simgrid::config::Flag<bool> _sg_mc_sleep_set{
@@ -142,11 +143,29 @@ simgrid::config::Flag<bool> _sg_mc_termination{
     "model-check/termination", "Whether to enable non progressive cycle detection", false,
     [](bool) { _mc_cfg_cb_check("value to enable/disable the detection of non progressive cycles"); }};
 
-bool simgrid::mc::cfg_use_DPOR()
+simgrid::mc::ReductionMode simgrid::mc::get_model_checking_reduction()
 {
-  if (cfg_mc_reduction.get() == "dpor" && _sg_mc_max_visited_states__ > 0) {
+  if ((cfg_mc_reduction.get() == "dpor" || cfg_mc_reduction.get() == "sdpor" || cfg_mc_reduction.get() == "odpor") &&
+      _sg_mc_max_visited_states__ > 0) {
     XBT_INFO("Disabling DPOR since state-equality reduction is activated with 'model-check/visited'");
-    return false;
+    return simgrid::mc::ReductionMode::none;
+  }
+
+  if (cfg_mc_reduction.get() == "none") {
+    return ReductionMode::none;
+  } else if (cfg_mc_reduction.get() == "dpor") {
+    return ReductionMode::dpor;
+  } else if (cfg_mc_reduction.get() == "sdpor") {
+    return ReductionMode::sdpor;
+  } else if (cfg_mc_reduction.get() == "odpor") {
+    return ReductionMode::odpor;
+  } else if (cfg_mc_reduction.get() == "udpor") {
+    XBT_INFO("No reduction will be used: "
+             "UDPOR has a dedicated invocation 'model-check/unfolding-checker' "
+             "but is not yet fully supported in SimGrid");
+    return ReductionMode::none;
+  } else {
+    XBT_INFO("Unknown reduction mode: defaulting to no reduction");
+    return ReductionMode::none;
   }
-  return cfg_mc_reduction.get() == "dpor";
 }
index 713156f..07d199f 100644 (file)
 
 /********************************** Configuration of MC **************************************/
 namespace simgrid::mc {
-bool cfg_use_DPOR(); // "model-check/reduction" == "DPOR"
+XBT_DECLARE_ENUM_CLASS(ReductionMode, none, dpor, sdpor, odpor);
 XBT_DECLARE_ENUM_CLASS(ModelCheckingMode, NONE, APP_SIDE, CHECKER_SIDE, REPLAY);
+ReductionMode get_model_checking_reduction(); // "model-check/reduction" == "DPOR"
 XBT_PUBLIC ModelCheckingMode get_model_checking_mode();
 XBT_PUBLIC void set_model_checking_mode(ModelCheckingMode mode);
-};
+}; // namespace simgrid::mc
 
 extern XBT_PUBLIC simgrid::config::Flag<std::string> _sg_mc_buffering;
 extern XBT_PRIVATE simgrid::config::Flag<int> _sg_mc_checkpoint;
index 2900710..16efc93 100644 (file)
@@ -37,7 +37,7 @@ public:
   std::size_t get_buffer_size() const { return sizeof(T); }
   operator T() const
   {
-    static_assert(std::is_trivial<T>::value, "Cannot convert non trivial type");
+    static_assert(std::is_trivial_v<T>, "Cannot convert non trivial type");
     return *get_buffer();
   }
   void clear() { std::memset(&buffer, 0, sizeof buffer); }
index 05e1d72..8de5bc9 100644 (file)
 
 #include "src/mc/sosp/PageStore.hpp"
 
-using simgrid::mc::PageStore;
-
 /***********************************/
 // a class to hold the variable used in the test cases
-class helper_tests {
-public:
-  static std::size_t pagesize;
-  static std::unique_ptr<PageStore> store;
-  static void* data;
-  static std::array<size_t, 4> pageno;
-  static int value;
+class pstore_test_helper {
+  const size_t pagesize = getpagesize();
+  simgrid::mc::PageStore store{50};
+  std::byte* data =
+      static_cast<std::byte*>(mmap(nullptr, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+  std::array<size_t, 4> pageno = {0, 0, 0, 0};
+  int value                    = 0;
+
+  void new_content(std::byte* buf, size_t size);
 
+public:
   // member functions used by the test suite(s)
-  static void Init();
-  static void store_page_once();
-  static void store_same_page();
-  static void store_new_page();
-  static void unref_pages();
-  static void reallocate_page();
-
-  static void new_content(void* buf, std::size_t size);
+  void init();
+  void store_page_once();
+  void store_same_page();
+  void store_new_page();
+  void unref_pages();
+  void reallocate_page();
 };
 
-// static member data initialization
-std::size_t helper_tests::pagesize             = 0;
-std::unique_ptr<PageStore> helper_tests::store = nullptr;
-void* helper_tests::data                       = nullptr;
-std::array<size_t, 4> helper_tests::pageno     = {{0, 0, 0, 0}};
-int helper_tests::value                        = 0;
-
-void helper_tests::Init()
+void pstore_test_helper::init()
 {
-  pagesize = (size_t)getpagesize();
-  store    = std::make_unique<simgrid::mc::PageStore>(50);
-  data     = mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-  REQUIRE(store->size() == 0);
+  REQUIRE(data != nullptr);
+  REQUIRE(store.size() == 0);
 }
 
-void helper_tests::store_page_once()
+void pstore_test_helper::store_page_once()
 {
   new_content(data, pagesize);
-  pageno[0] = store->store_page(data);
-  REQUIRE(store->get_ref(pageno[0]) == 1);
-  const void* copy = store->get_page(pageno[0]);
+  pageno[0] = store.store_page(data);
+  REQUIRE(store.get_ref(pageno[0]) == 1);
+  const auto* copy = store.get_page(pageno[0]);
   REQUIRE(::memcmp(data, copy, pagesize) == 0); // The page data should be the same
-  REQUIRE(store->size() == 1);
+  REQUIRE(store.size() == 1);
 }
 
-void helper_tests::store_same_page()
+void pstore_test_helper::store_same_page()
 {
-  pageno[1] = store->store_page(data);
+  pageno[1] = store.store_page(data);
   REQUIRE(pageno[0] == pageno[1]); // Page should be the same
-  REQUIRE(store->get_ref(pageno[0]) == 2);
-  REQUIRE(store->size() == 1);
+  REQUIRE(store.get_ref(pageno[0]) == 2);
+  REQUIRE(store.size() == 1);
 }
 
-void helper_tests::store_new_page()
+void pstore_test_helper::store_new_page()
 {
   new_content(data, pagesize);
-  pageno[2] = store->store_page(data);
+  pageno[2] = store.store_page(data);
   REQUIRE(pageno[0] != pageno[2]); // The new page should be different
-  REQUIRE(store->size() == 2);
+  REQUIRE(store.size() == 2);
 }
 
-void helper_tests::unref_pages()
+void pstore_test_helper::unref_pages()
 {
-  store->unref_page(pageno[0]);
-  REQUIRE(store->get_ref(pageno[0]) == 1);
-  REQUIRE(store->size() == 2);
+  store.unref_page(pageno[0]);
+  REQUIRE(store.get_ref(pageno[0]) == 1);
+  REQUIRE(store.size() == 2);
 
-  store->unref_page(pageno[1]);
-  REQUIRE(store->size() == 1);
+  store.unref_page(pageno[1]);
+  REQUIRE(store.size() == 1);
 }
 
-void helper_tests::reallocate_page()
+void pstore_test_helper::reallocate_page()
 {
   new_content(data, pagesize);
-  pageno[3] = store->store_page(data);
+  pageno[3] = store.store_page(data);
   REQUIRE(pageno[0] == pageno[3]); // The old page should be reused
-  REQUIRE(store->get_ref(pageno[3]) == 1);
-  REQUIRE(store->size() == 2);
+  REQUIRE(store.get_ref(pageno[3]) == 1);
+  REQUIRE(store.size() == 2);
 }
 
-void helper_tests::new_content(void* buf, std::size_t size)
+void pstore_test_helper::new_content(std::byte* buf, size_t size)
 {
   value++;
-  ::memset(buf, value, size);
+  std::fill_n(buf, size, static_cast<std::byte>(value));
 }
 
 TEST_CASE("MC page store, used during checkpoint", "MC::PageStore")
 {
-  helper_tests::Init();
+  pstore_test_helper pstore_test;
+  pstore_test.init();
+
   INFO("Store page once");
-  helper_tests::store_page_once();
+  pstore_test.store_page_once();
 
   INFO("Store the same page");
-  helper_tests::store_same_page();
+  pstore_test.store_same_page();
 
   INFO("Store a new page");
-  helper_tests::store_new_page();
+  pstore_test.store_new_page();
 
   INFO("Unref pages");
-  helper_tests::unref_pages();
+  pstore_test.unref_pages();
 
   INFO("Reallocate pages");
-  helper_tests::reallocate_page();
+  pstore_test.reallocate_page();
 }
index 09d865b..abf1dd0 100644 (file)
 #include <sys/mman.h>
 #include <xbt/random.hpp>
 
-/**************** Class BOOST_tests *************************/
-using simgrid::mc::Region;
 class snap_test_helper {
-  static simgrid::mc::PageStore page_store_;
-  static std::unique_ptr<simgrid::mc::RemoteProcessMemory> memory_;
+  simgrid::mc::PageStore page_store_{500};
+  simgrid::mc::RemoteProcessMemory memory_{getpid(), nullptr};
 
-public:
-  static void init_memory(void* mem, size_t size);
-  static void Init();
   struct prologue_return {
     size_t size;
-    void* src;
-    void* dstn;
-    std::unique_ptr<Region> region0;
-    std::unique_ptr<Region> region;
+    std::byte* src;
+    std::byte* dstn;
+    std::unique_ptr<simgrid::mc::Region> region0;
+    std::unique_ptr<simgrid::mc::Region> region;
   };
-  static prologue_return prologue(int n); // common to the below 5 fxs
-  static void read_whole_region();
-  static void read_region_parts();
-  static void compare_whole_region();
-  static void compare_region_parts();
-  static void read_pointer();
-
-  static void cleanup() { memory_ = nullptr; }
-};
+  prologue_return prologue(int n); // common to the below 5 fxs
+
+  static void init_memory(std::byte* mem, size_t size);
+
+public:
+  void read_whole_region();
+  void read_region_parts();
+  void compare_whole_region();
+  void compare_region_parts();
+  void read_pointer();
 
-// static member variables init.
-simgrid::mc::PageStore snap_test_helper::page_store_(500);
-std::unique_ptr<simgrid::mc::RemoteProcessMemory> snap_test_helper::memory_ = nullptr;
+  static void basic_requirements();
+};
 
-void snap_test_helper::init_memory(void* mem, size_t size)
+void snap_test_helper::init_memory(std::byte* mem, size_t size)
 {
-  auto* dest = static_cast<char*>(mem);
-  for (size_t i = 0; i < size; ++i) {
-    dest[i] = simgrid::xbt::random::uniform_int(0, 0xff);
-  }
+  std::generate_n(mem, size, []() { return static_cast<std::byte>(simgrid::xbt::random::uniform_int(0, 0xff)); });
 }
 
-void snap_test_helper::Init()
+void snap_test_helper::basic_requirements()
 {
   REQUIRE(xbt_pagesize == getpagesize());
   REQUIRE(1 << xbt_pagebits == xbt_pagesize);
-
-  memory_ = std::make_unique<simgrid::mc::RemoteProcessMemory>(getpid(), nullptr);
 }
 
 snap_test_helper::prologue_return snap_test_helper::prologue(int n)
 {
   // Store region page(s):
   size_t byte_size = n * xbt_pagesize;
-  void* source     = mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  auto* source =
+      static_cast<std::byte*>(mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
   INFO("Could not allocate source memory");
   REQUIRE(source != MAP_FAILED);
 
   // Init memory and take snapshots:
   init_memory(source, byte_size);
-  auto region0 = std::make_unique<simgrid::mc::Region>(page_store_, *memory_.get(), simgrid::mc::RegionType::Data,
-                                                       source, byte_size);
+  auto region0 =
+      std::make_unique<simgrid::mc::Region>(page_store_, memory_, simgrid::mc::RegionType::Data, source, byte_size);
   for (int i = 0; i < n; i += 2) {
-    init_memory((char*)source + i * xbt_pagesize, xbt_pagesize);
+    init_memory(source + i * xbt_pagesize, xbt_pagesize);
   }
-  auto region = std::make_unique<simgrid::mc::Region>(page_store_, *memory_.get(), simgrid::mc::RegionType::Data,
-                                                      source, byte_size);
+  auto region =
+      std::make_unique<simgrid::mc::Region>(page_store_, memory_, simgrid::mc::RegionType::Data, source, byte_size);
 
-  void* destination = mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  auto* destination =
+      static_cast<std::byte*>(mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
   INFO("Could not allocate destination memory");
   REQUIRE(destination != MAP_FAILED);
 
@@ -149,12 +141,12 @@ void snap_test_helper::compare_region_parts()
 }
 
 const int some_global_variable  = 42;
-const void* some_global_pointer = &some_global_variable;
+const void* const some_global_pointer = &some_global_variable;
 void snap_test_helper::read_pointer()
 {
   prologue_return ret = prologue(1);
   memcpy(ret.src, &some_global_pointer, sizeof(void*));
-  const simgrid::mc::Region region2(page_store_, *memory_.get(), simgrid::mc::RegionType::Data, ret.src, ret.size);
+  const simgrid::mc::Region region2(page_store_, memory_, simgrid::mc::RegionType::Data, ret.src, ret.size);
   INFO("Mismtach in MC_region_read_pointer()");
   REQUIRE(MC_region_read_pointer(&region2, ret.src) == some_global_pointer);
 
@@ -168,22 +160,22 @@ TEST_CASE("MC::Snapshot: A copy/snapshot of a given memory region", "MC::Snapsho
 {
   INFO("Sparse snapshot (using pages)");
 
-  snap_test_helper::Init();
+  snap_test_helper::basic_requirements();
+
+  snap_test_helper snap_test;
 
   INFO("Read whole region");
-  snap_test_helper::read_whole_region();
+  snap_test.read_whole_region();
 
   INFO("Read region parts");
-  snap_test_helper::read_region_parts();
+  snap_test.read_region_parts();
 
   INFO("Compare whole region");
-  snap_test_helper::compare_whole_region();
+  snap_test.compare_whole_region();
 
   INFO("Compare region parts");
-  snap_test_helper::compare_region_parts();
+  snap_test.compare_region_parts();
 
   INFO("Read pointer");
-  snap_test_helper::read_pointer();
-
-  snap_test_helper::cleanup();
+  snap_test.read_pointer();
 }
index c6e1533..463bfcb 100644 (file)
@@ -27,7 +27,17 @@ std::string ActorJoinTransition::to_string(bool verbose) const
 }
 bool ActorJoinTransition::depends(const Transition* other) const
 {
-  // Joining is indep with any other transitions:
+  // Joining is dependent with any transition whose
+  // actor is that of the `other` action. , Join i
+  if (other->aid_ == target_) {
+    return true;
+  }
+
+  // Actions executed by the same actor are always dependent
+  if (other->aid_ == aid_)
+    return true;
+
+  // Otherwise, joining is indep with any other transitions:
   // - It is only enabled once the target ends, and after this point it's enabled no matter what
   // - Other joins don't affect it, and it does not impact on the enabledness of any other transition
   return false;
index 4813a00..f532369 100644 (file)
@@ -35,6 +35,10 @@ std::string TestAnyTransition::to_string(bool verbose) const
 }
 bool TestAnyTransition::depends(const Transition* other) const
 {
+  // Actions executed by the same actor are always dependent
+  if (other->aid_ == aid_)
+    return true;
+
   return transitions_[times_considered_]->depends(other);
 }
 WaitAnyTransition::WaitAnyTransition(aid_t issuer, int times_considered, std::stringstream& stream)
@@ -57,6 +61,9 @@ std::string WaitAnyTransition::to_string(bool verbose) const
 }
 bool WaitAnyTransition::depends(const Transition* other) const
 {
+  // Actions executed by the same actor are always dependent
+  if (other->aid_ == aid_)
+    return true;
   return transitions_[times_considered_]->depends(other);
 }
 
index b374f19..029548d 100644 (file)
@@ -42,6 +42,10 @@ bool CommWaitTransition::depends(const Transition* other) const
   if (other->type_ < type_)
     return other->depends(this);
 
+  // Actions executed by the same actor are always dependent
+  if (other->aid_ == aid_)
+    return true;
+
   if (const auto* wait = dynamic_cast<const CommWaitTransition*>(other)) {
     if (timeout_ || wait->timeout_)
       return true; // Timeouts are not considered by the independence theorem, thus assumed dependent
@@ -80,6 +84,10 @@ bool CommTestTransition::depends(const Transition* other) const
   if (other->type_ < type_)
     return other->depends(this);
 
+  // Actions executed by the same actor are always dependent
+  if (other->aid_ == aid_)
+    return true;
+
   if (dynamic_cast<const CommTestTransition*>(other) != nullptr)
     return false; // Test & Test are independent
 
@@ -112,6 +120,10 @@ bool CommRecvTransition::depends(const Transition* other) const
   if (other->type_ < type_)
     return other->depends(this);
 
+  // Actions executed by the same actor are always dependent
+  if (other->aid_ == aid_)
+    return true;
+
   if (const auto* recv = dynamic_cast<const CommRecvTransition*>(other))
     return mbox_ == recv->mbox_;
 
@@ -164,6 +176,10 @@ bool CommSendTransition::depends(const Transition* other) const
   if (other->type_ < type_)
     return other->depends(this);
 
+  // Actions executed by the same actor are always dependent
+  if (other->aid_ == aid_)
+    return true;
+
   if (const auto* other_isend = dynamic_cast<const CommSendTransition*>(other))
     return mbox_ == other_isend->mbox_;
 
index 63a7d92..0936359 100644 (file)
@@ -34,6 +34,13 @@ std::string ObjectAccessTransition::to_string(bool verbose) const
 }
 bool ObjectAccessTransition::depends(const Transition* o) const
 {
+  if (o->type_ < type_)
+    return o->depends(this);
+
+  // Actions executed by the same actor are always dependent
+  if (o->aid_ == aid_)
+    return true;
+
   if (const auto* other = dynamic_cast<const ObjectAccessTransition*>(o))
     return objaddr_ == other->objaddr_; // dependent only if it's an access to the same object
   return false;
index ba00821..374d69d 100644 (file)
@@ -17,7 +17,13 @@ class RandomTransition : public Transition {
 public:
   std::string to_string(bool verbose) const override;
   RandomTransition(aid_t issuer, int times_considered, std::stringstream& stream);
-  bool depends(const Transition* other) const override { return aid_ == other->aid_; } // Independent with any other transition
+  bool depends(const Transition* other) const override
+  {
+    if (other->type_ < type_)
+      return other->depends(this);
+
+    return aid_ == other->aid_;
+  } // Independent with any other transition
 };
 
 } // namespace simgrid::mc
index a93e27b..dbd39a5 100644 (file)
@@ -63,6 +63,10 @@ bool MutexTransition::depends(const Transition* o) const
   if (o->type_ < type_)
     return o->depends(this);
 
+  // Actions executed by the same actor are always dependent
+  if (o->aid_ == aid_)
+    return true;
+
   // type_ <= other->type_ in  MUTEX_LOCK, MUTEX_TEST, MUTEX_TRYLOCK, MUTEX_UNLOCK, MUTEX_WAIT,
 
   if (auto* other = dynamic_cast<const MutexTransition*>(o)) {
index 15f1871..31b7fff 100644 (file)
@@ -412,49 +412,28 @@ static void on_host_creation(simgrid::s4u::Host& host)
   host.extension_set<FileDescriptorHostExt>(new FileDescriptorHostExt());
 }
 
-static void on_platform_created()
-{
-  for (auto const& host : simgrid::s4u::Engine::get_instance()->get_all_hosts()) {
-    const char* remote_disk_str = host->get_property("remote_disk");
-    if (not remote_disk_str)
-      continue;
-    std::vector<std::string> tokens;
-    boost::split(tokens, remote_disk_str, boost::is_any_of(":"));
-    std::string mount_point         = tokens[0];
-    simgrid::s4u::Host* remote_host = simgrid::s4u::Host::by_name_or_null(tokens[2]);
-    xbt_assert(remote_host, "You're trying to access a host that does not exist. Please check your platform file");
-
-    const simgrid::s4u::Disk* disk = nullptr;
-    for (auto const& d : remote_host->get_disks())
-      if (d->get_name() == tokens[1]) {
-        disk = d;
-        break;
-      }
-
-    xbt_assert(disk, "You're trying to mount a disk that does not exist. Please check your platform file");
-    disk->extension<FileSystemDiskExt>()->add_remote_mount(remote_host, mount_point);
-    host->add_disk(disk);
-
-    XBT_DEBUG("Host '%s' wants to mount a remote disk: %s of %s mounted on %s", host->get_cname(), disk->get_cname(),
-              remote_host->get_cname(), mount_point.c_str());
-    XBT_DEBUG("Host '%s' now has %zu disks", host->get_cname(), host->get_disks().size());
-  }
-}
-
-static void on_simulation_end()
-{
-  XBT_DEBUG("Simulation is over, time to unregister remote disks if any");
-  for (auto const& host : simgrid::s4u::Engine::get_instance()->get_all_hosts()) {
-    const char* remote_disk_str = host->get_property("remote_disk");
-    if (remote_disk_str) {
-      std::vector<std::string> tokens;
-      boost::split(tokens, remote_disk_str, boost::is_any_of(":"));
-      XBT_DEBUG("Host '%s' wants to unmount a remote disk: %s of %s mounted on %s", host->get_cname(),
-                tokens[1].c_str(), tokens[2].c_str(), tokens[0].c_str());
-      host->remove_disk(tokens[1]);
-      XBT_DEBUG("Host '%s' now has %zu disks", host->get_cname(), host->get_disks().size());
-    }
-  }
+ static void on_platform_created()
+ {
+   for (auto const& host : simgrid::s4u::Engine::get_instance()->get_all_hosts()) {
+     const char* remote_disk_str = host->get_property("remote_disk");
+     if (not remote_disk_str)
+       continue;
+     std::vector<std::string> tokens;
+     boost::split(tokens, remote_disk_str, boost::is_any_of(":"));
+     std::string mount_point = tokens[0];
+     simgrid::s4u::Host* remote_host = simgrid::s4u::Host::by_name_or_null(tokens[2]);
+     xbt_assert(remote_host, "You're trying to access a host that does not exist. Please check your platform file");
+
+     const simgrid::s4u::Disk* disk = nullptr;
+     for (auto const& d : remote_host->get_disks())
+       if (d->get_name() == tokens[1]) {
+         disk = d;
+         break;
+       }
+
+     xbt_assert(disk, "You're trying to mount a disk that does not exist. Please check your platform file");
+     disk->extension<FileSystemDiskExt>()->add_remote_mount(remote_host, mount_point);
+   }
 }
 
 /* **************************** Public interface *************************** */
@@ -481,7 +460,6 @@ void sg_storage_file_system_init()
     simgrid::s4u::Host::on_creation_cb(&on_host_creation);
   }
   simgrid::s4u::Engine::on_platform_created_cb(&on_platform_created);
-  simgrid::s4u::Engine::on_simulation_end_cb(&on_simulation_end);
 }
 
 sg_file_t sg_file_open(const char* fullpath, void* data)
index 0a5ac58..138cdae 100644 (file)
@@ -289,21 +289,18 @@ public:
       if (activity.get_host() == get_host())
         pre_task();
     });
-    simgrid::s4u::Activity::on_completion_cb([this](simgrid::s4u::Activity const& activity) {
-      const auto* exec = dynamic_cast<simgrid::s4u::Exec const*>(&activity);
-      if (exec == nullptr) // Only Execs are concerned here
-        return;
+    simgrid::s4u::Exec::on_completion_cb([this](simgrid::s4u::Exec const& exec) {
       // For more than one host (not yet supported), we can access the host via
       // simcalls_.front()->issuer->get_iface()->get_host()
-      if (exec->get_host() == get_host() && iteration_running) {
-        comp_timer += exec->get_finish_time() - exec->get_start_time();
+      if (exec.get_host() == get_host() && iteration_running) {
+        comp_timer += exec.get_finish_time() - exec.get_start_time();
       }
     });
     // FIXME I think that this fires at the same time for all hosts, so when the src sends something,
     // the dst will be notified even though it didn't even arrive at the recv yet
-    kernel::activity::CommImpl::on_start.connect([this](const kernel::activity::CommImpl& comm) {
-      const auto* act = static_cast<kernel::resource::NetworkAction*>(comm.model_action_);
-      if ((get_host() == &act->get_src() || get_host() == &act->get_dst()) && iteration_running) {
+    simgrid::s4u::Comm::on_start_cb([this](const s4u::Comm& comm) {
+      if ((get_host() == comm.get_sender()->get_host() || get_host() == comm.get_receiver()->get_host()) &&
+           iteration_running) {
         post_task();
       }
     });
index 2bf95b1..d2f5dc8 100644 (file)
@@ -11,6 +11,7 @@
 #include <simgrid/s4u/VirtualMachine.hpp>
 #include <simgrid/simix.hpp>
 
+#include "src/kernel/activity/ActivityImpl.hpp"
 #include "src/kernel/resource/CpuImpl.hpp"
 #include "src/simgrid/module.hpp"
 
@@ -420,7 +421,7 @@ static void on_creation(simgrid::s4u::Host& host)
 static void on_action_state_change(simgrid::kernel::resource::CpuAction const& action,
                                    simgrid::kernel::resource::Action::State /*previous*/)
 {
-  for (simgrid::kernel::resource::CpuImpl* const& cpu : action.cpus()) {
+  for (simgrid::kernel::resource::CpuImpl const* cpu : action.cpus()) {
     simgrid::s4u::Host* host = cpu->get_iface();
     if (host != nullptr) {
       // If it's a VM, take the corresponding PM
@@ -438,14 +439,13 @@ static void on_action_state_change(simgrid::kernel::resource::CpuAction const& a
 
 /* This callback is fired either when the host changes its state (on/off) ("onStateChange") or its speed
  * (because the user changed the pstate, or because of external trace events) ("onSpeedChange") */
-static void on_host_change(simgrid::s4u::Host const& host)
+static void on_host_change(simgrid::s4u::Host const& h)
 {
-  if (dynamic_cast<simgrid::s4u::VirtualMachine const*>(&host)) // Ignore virtual machines
-    return;
-
-  auto* host_energy = host.extension<HostEnergy>();
+  auto* host = &h;
+  if (const auto* vm = dynamic_cast<simgrid::s4u::VirtualMachine const*>(host)) // Take the PM of virtual machines
+    host = vm->get_pm();
 
-  host_energy->update();
+  host->extension<HostEnergy>()->update();
 }
 
 static void on_host_destruction(simgrid::s4u::Host const& host)
@@ -473,6 +473,13 @@ static void on_simulation_end()
            used_hosts_energy, total_energy - used_hosts_energy);
 }
 
+static void on_activity_suspend_resume(simgrid::s4u::Activity const& activity)
+{
+  if (auto* action = dynamic_cast<simgrid::kernel::resource::CpuAction*>(activity.get_impl()->model_action_);
+      action != nullptr)
+    on_action_state_change(*action, /*ignored*/ action->get_state());
+}
+
 /* **************************** Public interface *************************** */
 
 /** @ingroup plugin_host_energy
@@ -487,20 +494,24 @@ void sg_host_energy_plugin_init()
   HostEnergy::EXTENSION_ID = simgrid::s4u::Host::extension_create<HostEnergy>();
 
   simgrid::s4u::Host::on_creation_cb(&on_creation);
-  simgrid::s4u::Host::on_state_change_cb(&on_host_change);
+  simgrid::s4u::Host::on_onoff_cb(&on_host_change);
   simgrid::s4u::Host::on_speed_change_cb(&on_host_change);
   simgrid::s4u::Host::on_destruction_cb(&on_host_destruction);
+  simgrid::s4u::Host::on_exec_state_change_cb(&on_action_state_change);
+  simgrid::s4u::VirtualMachine::on_suspend_cb(&on_host_change);
+  simgrid::s4u::VirtualMachine::on_resume_cb(&on_host_change);
+  simgrid::s4u::Exec::on_suspend_cb(on_activity_suspend_resume);
+  simgrid::s4u::Exec::on_resume_cb(on_activity_suspend_resume);
   simgrid::s4u::Engine::on_simulation_end_cb(&on_simulation_end);
-  simgrid::kernel::resource::CpuAction::on_state_change.connect(&on_action_state_change);
   // We may only have one actor on a node. If that actor executes something like
   //   compute -> recv -> compute
-  // the recv operation will not trigger a "CpuAction::on_state_change". This means
+  // the recv operation will not trigger a "Host::on_exec_state_change_cb". This means
   // that the next trigger would be the 2nd compute, hence ignoring the idle time
   // during the recv call. By updating at the beginning of a compute, we can
   // fix that. (If the cpu is not idle, this is not required.)
   simgrid::s4u::Exec::on_start_cb([](simgrid::s4u::Exec const& activity) {
     if (activity.get_host_number() == 1) { // We only run on one host
-      simgrid::s4u::Host* host         = activity.get_host();
+      simgrid::s4u::Host* host = activity.get_host();
       if (const auto* vm = dynamic_cast<simgrid::s4u::VirtualMachine*>(host))
         host = vm->get_pm();
       xbt_assert(host != nullptr);
index 6479b3a..c0eb520 100644 (file)
@@ -24,8 +24,7 @@ It attaches an extension to each host to store some data, and places callbacks i
   - :cpp:func:`simgrid::s4u::Host::on_creation_cb`: Attach a new extension to the newly created host.
   - :cpp:func:`simgrid::s4u::Exec::on_start_cb`: Make note that a new execution started, increasing the load.
   - :cpp:func:`simgrid::s4u::Exec::on_completion_cb`: Make note that an execution completed, decreasing the load.
-  - :cpp:func:`simgrid::s4u::Host::on_state_change_cb`: Do what is appropriate when the host gets suspended, turned off
-    or similar.
+  - :cpp:func:`simgrid::s4u::Host::on_onoff_cb`: Do what is appropriate when the host gets turned off or on.
   - :cpp:func:`simgrid::s4u::Host::on_speed_change_cb`: Do what is appropriate when the DVFS is modified.
 
   Note that extensions are automatically destroyed when the host gets destroyed.
@@ -247,12 +246,9 @@ void sg_host_load_plugin_init()
       XBT_WARN("HostLoad plugin currently does not support executions on several hosts");
     }
   });
-  simgrid::s4u::Activity::on_completion_cb([](simgrid::s4u::Activity const& activity) {
-    const auto* exec = dynamic_cast<simgrid::s4u::Exec const*>(&activity);
-    if (exec == nullptr) // Only Execs are concerned here
-      return;
-    if (exec->get_host_number() == 1) { // We only run on one host
-      simgrid::s4u::Host* host               = exec->get_host();
+  simgrid::s4u::Exec::on_completion_cb([](simgrid::s4u::Exec const& exec) {
+    if (exec.get_host_number() == 1) { // We only run on one host
+      simgrid::s4u::Host* host = exec.get_host();
       if (const auto* vm = dynamic_cast<simgrid::s4u::VirtualMachine*>(host))
         host = vm->get_pm();
       xbt_assert(host != nullptr);
@@ -261,7 +257,7 @@ void sg_host_load_plugin_init()
       XBT_WARN("HostLoad plugin currently does not support executions on several hosts");
     }
   });
-  simgrid::s4u::Host::on_state_change_cb(&on_host_change);
+  simgrid::s4u::Host::on_onoff_cb(&on_host_change);
   simgrid::s4u::Host::on_speed_change_cb(&on_host_change);
 }
 
index 4cf3a5e..66ab5d3 100644 (file)
@@ -6,6 +6,7 @@
 #include "simgrid/Exception.hpp"
 #include "simgrid/host.h"
 #include "simgrid/plugins/energy.h"
+#include "simgrid/s4u/Comm.hpp"
 #include "simgrid/s4u/Engine.hpp"
 #include "simgrid/s4u/Link.hpp"
 #include "src/kernel/activity/CommImpl.hpp"
@@ -161,15 +162,17 @@ static void on_simulation_end()
   XBT_INFO("Total energy over all links: %f", total_energy);
 }
 
-static void on_communication(const simgrid::kernel::activity::CommImpl& comm)
+static void on_communication(const simgrid::s4u::Comm& comm)
 {
-  for (auto const* link : comm.get_traversed_links()) {
+  auto* pimpl = static_cast<simgrid::kernel::activity::CommImpl*>(comm.get_impl());
+  for (auto const* link : pimpl->get_traversed_links()) {
     if (link != nullptr && link->get_sharing_policy() != simgrid::s4u::Link::SharingPolicy::WIFI) {
       XBT_DEBUG("Update %s on Comm Start/End", link->get_cname());
       link->extension<LinkEnergy>()->update();
     }
   }
 }
+
 /* **************************** Public interface *************************** */
 
 int sg_link_energy_is_inited()
@@ -198,7 +201,7 @@ void sg_link_energy_plugin_init()
     }
   });
 
-  simgrid::s4u::Link::on_state_change_cb([](simgrid::s4u::Link const& link) {
+  simgrid::s4u::Link::on_onoff_cb([](simgrid::s4u::Link const& link) {
     if (link.get_sharing_policy() != simgrid::s4u::Link::SharingPolicy::WIFI)
       link.extension<LinkEnergy>()->update();
   });
@@ -209,8 +212,8 @@ void sg_link_energy_plugin_init()
                link.extension<LinkEnergy>()->get_consumed_energy());
   });
 
-  simgrid::kernel::activity::CommImpl::on_start.connect(&on_communication);
-  simgrid::kernel::activity::CommImpl::on_completion.connect(&on_communication);
+  simgrid::s4u::Comm::on_start_cb(&on_communication);
+  simgrid::s4u::Comm::on_completion_cb(&on_communication);
 
   simgrid::s4u::Engine::on_simulation_end_cb(&on_simulation_end);
 }
index 0eb9cf0..3912fc9 100644 (file)
@@ -4,6 +4,7 @@
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
 #include <simgrid/plugins/energy.h>
+#include <simgrid/s4u/Comm.hpp>
 #include <simgrid/s4u/Engine.hpp>
 #include <simgrid/s4u/Link.hpp>
 
@@ -265,9 +266,10 @@ void LinkEnergyWifi::init_watts_range_list()
 
 using simgrid::plugin::LinkEnergyWifi;
 /* **************************** events  callback *************************** */
-static void on_communication(const simgrid::kernel::activity::CommImpl& comm)
+static void on_communication(const simgrid::s4u::Comm& comm)
 {
-  for (const auto* link : comm.get_traversed_links()) {
+  auto* pimpl = static_cast<simgrid::kernel::activity::CommImpl*>(comm.get_impl());
+  for (auto const* link : pimpl->get_traversed_links()) {
     if (link != nullptr && link->get_sharing_policy() == simgrid::s4u::Link::SharingPolicy::WIFI) {
       auto* link_energy = link->extension<LinkEnergyWifi>();
       XBT_DEBUG("Update %s on Comm Start/End", link->get_cname());
@@ -324,6 +326,6 @@ void sg_wifi_energy_plugin_init()
     }
   });
 
-  simgrid::kernel::activity::CommImpl::on_start.connect(&on_communication);
-  simgrid::kernel::activity::CommImpl::on_completion.connect(&on_communication);
+  simgrid::s4u::Comm::on_start_cb(&on_communication);
+  simgrid::s4u::Comm::on_completion_cb(&on_communication);
 }
index 8614765..8f0937d 100644 (file)
@@ -4,6 +4,7 @@
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
 #include <simgrid/plugins/load.h>
+#include <simgrid/s4u/Comm.hpp>
 #include <simgrid/s4u/Engine.hpp>
 
 #include "src/kernel/activity/CommImpl.hpp"
@@ -161,9 +162,10 @@ double LinkLoad::get_average_bytes()
 using simgrid::plugin::LinkLoad;
 
 /* **************************** events  callback *************************** */
-static void on_communication(const simgrid::kernel::activity::CommImpl& comm)
+static void on_communication(const simgrid::s4u::Comm& comm)
 {
-  for (const auto* link : comm.get_traversed_links()) {
+  auto* pimpl = static_cast<simgrid::kernel::activity::CommImpl*>(comm.get_impl());
+  for (auto const* link : pimpl->get_traversed_links()) {
     if (link != nullptr && link->get_sharing_policy() != simgrid::s4u::Link::SharingPolicy::WIFI) {
       auto* link_load = link->extension<LinkLoad>();
       XBT_DEBUG("Update %s on Comm Start/End", link->get_cname());
@@ -199,10 +201,10 @@ void sg_link_load_plugin_init()
   });
 
   // Call this plugin on some of the links' events.
-  simgrid::kernel::activity::CommImpl::on_start.connect(&on_communication);
-  simgrid::kernel::activity::CommImpl::on_completion.connect(&on_communication);
+  simgrid::s4u::Comm::on_start_cb(&on_communication);
+  simgrid::s4u::Comm::on_completion_cb(&on_communication);
 
-  simgrid::s4u::Link::on_state_change_cb([](simgrid::s4u::Link const& link) {
+  simgrid::s4u::Link::on_onoff_cb([](simgrid::s4u::Link const& link) {
     if (link.get_sharing_policy() != simgrid::s4u::Link::SharingPolicy::WIFI) {
       auto link_load = link.extension<LinkLoad>();
       if (link_load->is_tracked())
diff --git a/src/plugins/operation.cpp b/src/plugins/operation.cpp
deleted file mode 100644 (file)
index 32ed866..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-#include <simgrid/Exception.hpp>
-#include <simgrid/plugins/operation.hpp>
-#include <simgrid/s4u/Comm.hpp>
-#include <simgrid/s4u/Exec.hpp>
-#include <simgrid/simix.hpp>
-
-#include "src/simgrid/module.hpp"
-
-SIMGRID_REGISTER_PLUGIN(operation, "Battery management", nullptr)
-/** @defgroup plugin_operation plugin_operation Plugin Operation
-
-  @beginrst
-
-This is the operation plugin, enabling management of Operations.
-To activate this plugin, first call :cpp:func:`Operation::init`.
-
-Operations are designed to represent workflows, i.e, graphs of Operations.
-Operations can only be instancied using either
-:cpp:func:`simgrid::plugins::ExecOp::init` or :cpp:func:`simgrid::plugins::CommOp::init`
-An ExecOp is an Execution Operation. Its underlying Activity is an :ref:`Exec <API_s4u_Exec>`.
-A CommOp is a Communication Operation. Its underlying Activity is a :ref:`Comm <API_s4u_Comm>`.
-
-  @endrst
- */
-XBT_LOG_NEW_DEFAULT_SUBCATEGORY(Operation, kernel, "Logging specific to the operation plugin");
-
-namespace simgrid::plugins {
-
-xbt::signal<void(Operation*)> Operation::on_start;
-xbt::signal<void(Operation*)> Operation::on_end;
-
-Operation::Operation(const std::string& name) : name_(name) {}
-
-/**
- *  @param predecessor The Operation to add.
- *  @brief Add a predecessor to this Operation.
- */
-void Operation::add_predecessor(Operation* predecessor)
-{
-  if (predecessors_.find(predecessor) == predecessors_.end())
-    simgrid::kernel::actor::simcall_answered([this, predecessor] { predecessors_[predecessor] = 0; });
-}
-
-/**
- *  @param predecessor The Operation to remove.
- *  @brief Remove a predecessor from this Operation.
- */
-void Operation::remove_predecessor(Operation* predecessor)
-{
-  simgrid::kernel::actor::simcall_answered([this, predecessor] { predecessors_.erase(predecessor); });
-}
-
-/**
- *  @brief Return True if the Operation can start a new Activity.
- *  @note The Operation is ready if not already doing something and there is at least one execution waiting in queue.
- */
-bool Operation::ready_to_run() const
-{
-  return not working_ && queued_execs_ > 0;
-}
-
-/**
- *  @param source The sender.
- *  @brief Receive a token from another Operation.
- *  @note Check upon reception if the Operation has received a token from each of its predecessors,
- * and in this case consumes those tokens and enqueue an execution.
- */
-void Operation::receive(Operation* source)
-{
-  XBT_DEBUG("Operation %s received a token from %s", name_.c_str(), source->name_.c_str());
-  auto it = predecessors_.find(source);
-  simgrid::kernel::actor::simcall_answered([this, it] {
-    it->second++;
-    bool enough_tokens = true;
-    for (auto const& [key, val] : predecessors_)
-      if (val < 1) {
-        enough_tokens = false;
-        break;
-      }
-    if (enough_tokens) {
-      for (auto& [key, val] : predecessors_)
-        val--;
-      enqueue_execs(1);
-    }
-  });
-}
-
-/**
- *  @brief Operation routine when finishing an execution.
- *  @note Set its working status as false.
- * Add 1 to its count of finished executions.
- * Call the on_this_end func.
- * Fire on_end callback.
- * Send a token to each of its successors.
- * Start a new execution if possible.
- */
-void Operation::complete()
-{
-  simgrid::kernel::actor::simcall_answered([this] {
-    working_ = false;
-    count_++;
-  });
-  if (end_func_)
-    end_func_(this);
-  Operation::on_end(this);
-  for (auto const& op : successors_)
-    op->receive(this);
-  if (ready_to_run())
-    execute();
-}
-
-/** @ingroup plugin_operation
- *  @brief Init the Operation plugin.
- *  @note Add a completion callback to all Activities to call Operation::complete().
- */
-void Operation::init()
-{
-  if (Operation::inited_)
-    return;
-  Operation::inited_                      = true;
-  ExtendedAttributeActivity::EXTENSION_ID = simgrid::s4u::Activity::extension_create<ExtendedAttributeActivity>();
-  simgrid::s4u::Activity::on_completion_cb([](simgrid::s4u::Activity const& activity) {
-    activity.extension<ExtendedAttributeActivity>()->operation_->complete();
-  });
-}
-
-/** @ingroup plugin_operation
- *  @param n The number of executions to enqueue.
- *  @brief Enqueue executions.
- *  @note Immediatly starts an execution if possible.
- */
-void Operation::enqueue_execs(int n)
-{
-  simgrid::kernel::actor::simcall_answered([this, n] {
-    queued_execs_ += n;
-    if (ready_to_run())
-      execute();
-  });
-}
-
-/** @ingroup plugin_operation
- *  @param amount The amount to set.
- *  @brief Set the amout of work to do.
- *  @note Amount in flop for ExecOp and in bytes for CommOp.
- */
-void Operation::set_amount(double amount)
-{
-  simgrid::kernel::actor::simcall_answered([this, amount] { amount_ = amount; });
-}
-
-/** @ingroup plugin_operation
- *  @param successor The Operation to add.
- *  @brief Add a successor to this Operation.
- *  @note It also adds this as a predecessor of successor.
- */
-void Operation::add_successor(OperationPtr successor)
-{
-  simgrid::kernel::actor::simcall_answered([this, successor] { successors_.insert(successor.get()); });
-  successor->add_predecessor(this);
-}
-
-/** @ingroup plugin_operation
- *  @param successor The Operation to remove.
- *  @brief Remove a successor from this Operation.
- *  @note It also remove this from the predecessors of successor.
- */
-void Operation::remove_successor(OperationPtr successor)
-{
-  simgrid::kernel::actor::simcall_answered([this, successor] { successors_.erase(successor.get()); });
-  successor->remove_predecessor(this);
-}
-
-void Operation::remove_all_successors()
-{
-  simgrid::kernel::actor::simcall_answered([this] {
-    while (not successors_.empty()) {
-      auto* successor = *(successors_.begin());
-      successor->predecessors_.erase(this);
-      successors_.erase(successor);
-    }
-  });
-}
-
-/** @ingroup plugin_operation
- *  @param func The function to set.
- *  @brief Set a function to be called before each execution.
- *  @note The function is called before the underlying Activity starts.
- */
-void Operation::on_this_start(const std::function<void(Operation*)>& func)
-{
-  simgrid::kernel::actor::simcall_answered([this, &func] { start_func_ = func; });
-}
-
-/** @ingroup plugin_operation
- *  @param func The function to set.
- *  @brief Set a function to be called after each execution.
- *  @note The function is called after the underlying Activity ends, but before sending tokens to successors.
- */
-void Operation::on_this_end(const std::function<void(Operation*)>& func)
-{
-  simgrid::kernel::actor::simcall_answered([this, &func] { end_func_ = func; });
-}
-
-/** @ingroup plugin_operation
- *  @brief Return the number of completed executions.
- */
-int Operation::get_count() const
-{
-  return count_;
-}
-
-/**
- *  @brief Default constructor.
- */
-ExecOp::ExecOp(const std::string& name) : Operation(name) {}
-
-/** @ingroup plugin_operation
- *  @brief Smart Constructor.
- */
-ExecOpPtr ExecOp::init(const std::string& name)
-{
-  return ExecOpPtr(new ExecOp(name));
-}
-
-/** @ingroup plugin_operation
- *  @brief Smart Constructor.
- */
-ExecOpPtr ExecOp::init(const std::string& name, double flops, s4u::Host* host)
-{
-  return init(name)->set_flops(flops)->set_host(host);
-}
-
-/**
- *  @brief Do one execution of the Operation.
- *  @note Call the on_this_start() func. Set its working status as true.
- *  Init and start the underlying Activity.
- */
-void ExecOp::execute()
-{
-  if (start_func_)
-    start_func_(this);
-  Operation::on_start(this);
-  kernel::actor::simcall_answered([this] {
-    working_      = true;
-    queued_execs_ = std::max(queued_execs_ - 1, 0);
-  });
-  s4u::ExecPtr exec = s4u::Exec::init();
-  exec->set_name(name_);
-  exec->set_flops_amount(amount_);
-  exec->set_host(host_);
-  exec->start();
-  exec->extension_set(new ExtendedAttributeActivity());
-  exec->extension<ExtendedAttributeActivity>()->operation_ = this;
-  kernel::actor::simcall_answered([this, exec] { current_activity_ = exec; });
-}
-
-/** @ingroup plugin_operation
- *  @param host The host to set.
- *  @brief Set a new host.
- */
-ExecOpPtr ExecOp::set_host(s4u::Host* host)
-{
-  kernel::actor::simcall_answered([this, host] { host_ = host; });
-  return this;
-}
-
-/** @ingroup plugin_operation
- *  @param flops The amount of flops to set.
- */
-ExecOpPtr ExecOp::set_flops(double flops)
-{
-  kernel::actor::simcall_answered([this, flops] { amount_ = flops; });
-  return this;
-}
-
-/**
- *  @brief Default constructor.
- */
-CommOp::CommOp(const std::string& name) : Operation(name) {}
-
-/** @ingroup plugin_operation
- *  @brief Smart constructor.
- */
-CommOpPtr CommOp::init(const std::string& name)
-{
-  return CommOpPtr(new CommOp(name));
-}
-
-/** @ingroup plugin_operation
- *  @brief Smart constructor.
- */
-CommOpPtr CommOp::init(const std::string& name, double bytes, s4u::Host* source,
-                       s4u::Host* destination)
-{
-  return init(name)->set_bytes(bytes)->set_source(source)->set_destination(destination);
-}
-
-/**
- *  @brief Do one execution of the Operation.
- *  @note Call the on_this_start() func. Set its working status as true.
- *  Init and start the underlying Activity.
- */
-void CommOp::execute()
-{
-  if (start_func_)
-    start_func_(this);
-  Operation::on_start(this);
-  kernel::actor::simcall_answered([this] {
-    working_      = true;
-    queued_execs_ = std::max(queued_execs_ - 1, 0);
-  });
-  s4u::CommPtr comm = s4u::Comm::sendto_init(source_, destination_);
-  comm->set_name(name_);
-  comm->set_payload_size(amount_);
-  comm->start();
-  comm->extension_set(new ExtendedAttributeActivity());
-  comm->extension<ExtendedAttributeActivity>()->operation_ = this;
-  kernel::actor::simcall_answered([this, comm] { current_activity_ = comm; });
-}
-
-/** @ingroup plugin_operation
- *  @param source The host to set.
- *  @brief Set a new source host.
- */
-CommOpPtr CommOp::set_source(s4u::Host* source)
-{
-  kernel::actor::simcall_answered([this, source] { source_ = source; });
-  return this;
-}
-
-/** @ingroup plugin_operation
- *  @param destination The host to set.
- *  @brief Set a new destination host.
- */
-CommOpPtr CommOp::set_destination(s4u::Host* destination)
-{
-  kernel::actor::simcall_answered([this, destination] { destination_ = destination; });
-  return this;
-}
-
-/** @ingroup plugin_operation
- *  @param bytes The amount of bytes to set.
- */
-CommOpPtr CommOp::set_bytes(double bytes)
-{
-  kernel::actor::simcall_answered([this, bytes] { amount_ = bytes; });
-  return this;
-}
-
-} // namespace simgrid::plugins
-
-simgrid::xbt::Extension<simgrid::s4u::Activity, simgrid::plugins::ExtendedAttributeActivity>
-    simgrid::plugins::ExtendedAttributeActivity::EXTENSION_ID;
-bool simgrid::plugins::Operation::inited_ = false;
diff --git a/src/plugins/photovoltaic.cpp b/src/plugins/photovoltaic.cpp
new file mode 100644 (file)
index 0000000..5a4cd33
--- /dev/null
@@ -0,0 +1,287 @@
+#include <simgrid/Exception.hpp>
+#include <simgrid/plugins/photovoltaic.hpp>
+#include <simgrid/s4u/Actor.hpp>
+#include <simgrid/s4u/Engine.hpp>
+#include <simgrid/s4u/Host.hpp>
+#include <simgrid/s4u/VirtualMachine.hpp>
+#include <simgrid/simix.hpp>
+
+#include "src/kernel/resource/CpuImpl.hpp"
+#include "src/simgrid/module.hpp"
+
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/split.hpp>
+
+SIMGRID_REGISTER_PLUGIN(photovoltaic, "Photovoltaic management", &sg_photovoltaic_plugin_init)
+
+/** @defgroup plugin_photovoltaic plugin_photovoltaic Plugin photovoltaic
+
+  @beginrst
+
+This is the photovoltaic plugin, enabling management of photovoltaic panels on hosts.
+To activate this plugin, first call :cpp:func:`sg_photovoltaic_plugin_init()`.
+
+This plugin allows evaluation of photovoltaic panels power generation during simulation depending on size, solar
+irradiance and conversion factor.
+
+The power model is taken from the paper `"Reinforcement Learning Based Load Balancing for
+Geographically Distributed Data Centres" <https://dro.dur.ac.uk/33395/1/33395.pdf?DDD280+kkgc95+vbdv77>`_ by Max Mackie et. al.
+
+The cost model is taken from the chapter 4 of the thesis `Sizing and Operation of Multi-Energy Hydrogen-Based
+Microgrids <https://tel.archives-ouvertes.fr/tel-02077668/document>`_ by Bei Li.
+
+Photovoltaic Panel properties
+....................
+
+Properties of panels are defined as properties of hosts in the platform XML file.
+
+Here is an example of XML declaration where we consider a host as a photovoltaic panel:
+
+.. code-block:: xml
+
+   <host id="pv_panel" speed="0f">
+      <prop id="photovoltaic_area" value="4" />
+      <prop id="photovoltaic_conversion_efficiency" value="0.2" />
+    </host>
+
+The different properties are:
+
+- ``photovoltaic_area``: Set the area of the panel in m² (default=0)
+- ``photovoltaic_conversion_efficiency``: Set the conversion efficiency of the panel (default=0)
+- ``photovoltaic_solar_irradiance``: Set the initial solar irradiance in W/m² (default=0)
+- ``photovoltaic_min_power``: Set the minimum power of the panel in W (default=-1). Power generation below this value is automatically 0. Value is ignored if negative
+- ``photovoltaic_max_power``: Set the maximal power of the panel in W (default=-1). Power generation above this value is automatically truncated. Value is ignored if negative
+- ``photovoltaic_eval_cost``: Evaluate the cost of the panel during the simulation if set to 1 (defaulf=0)
+- ``photovoltaic_lifespan``: Set the lifespan of the panel in years (default=0)
+- ``photovoltaic_investment_cost``: Set the investment cost of the panel (default=0)
+- ``photovoltaic_maintenance_cost``: Set the maintenance cost of the panel (default=0)
+
+  @endrst
+ */
+
+XBT_LOG_NEW_DEFAULT_SUBCATEGORY(photovoltaic, kernel, "Logging specific to the photovoltaic plugin");
+
+namespace simgrid::plugin {
+class Photovoltaic {
+private:
+  simgrid::s4u::Host* host_ = nullptr;
+
+  double area_m2_                   = 0;
+  double conversion_efficiency_     = 0;
+  double solar_irradiance_w_per_m2_ = 0;
+  double min_power_w_               = -1;
+  double max_power_w_               = -1;
+
+  double power_w_      = 0;
+  double last_updated_ = 0;
+
+  bool eval_cost_                 = false;
+  double cumulative_cost_         = 0;
+  int lifespan_years_             = 0;
+  double investment_cost_per_w_   = 0;
+  double maintenance_cost_per_wh_ = 0;
+
+  void init_photovoltaic_params();
+  void init_cost_params();
+  void update();
+
+  Photovoltaic* set_area(double a);
+  Photovoltaic* set_conversion_efficiency(double e);
+  Photovoltaic* set_min_power(double p);
+  Photovoltaic* set_max_power(double p);
+  Photovoltaic* set_eval_cost(bool eval);
+  Photovoltaic* set_lifespan(int l);
+  Photovoltaic* set_investment_cost(double c);
+  Photovoltaic* set_maintenance_cost(double c);
+
+public:
+  static simgrid::xbt::Extension<simgrid::s4u::Host, Photovoltaic> EXTENSION_ID;
+
+  explicit Photovoltaic(simgrid::s4u::Host* host);
+  ~Photovoltaic();
+
+  Photovoltaic* set_solar_irradiance(double s);
+
+  double get_power();
+};
+
+Photovoltaic* Photovoltaic::set_area(double a)
+{
+  xbt_assert(a > 0, " : area should be > 0 (provided: %f)", a);
+  simgrid::kernel::actor::simcall_answered([this, a] { area_m2_ = a; });
+  return this;
+}
+
+Photovoltaic* Photovoltaic::set_conversion_efficiency(double e)
+{
+  xbt_assert(e > 0 and e <= 1, " : conversion efficiency should be in [0,1] (provided: %f)", e);
+  simgrid::kernel::actor::simcall_answered([this, e] { conversion_efficiency_ = e; });
+  return this;
+}
+
+Photovoltaic* Photovoltaic::set_solar_irradiance(double s)
+{
+  xbt_assert(s > 0, " : solar irradiance should be > 0 (provided: %f)", s);
+  simgrid::kernel::actor::simcall_answered([this, s] { solar_irradiance_w_per_m2_ = s; });
+  return this;
+}
+
+Photovoltaic* Photovoltaic::set_min_power(double p)
+{
+  simgrid::kernel::actor::simcall_answered([this, p] { min_power_w_ = p; });
+  return this;
+}
+
+Photovoltaic* Photovoltaic::set_max_power(double p)
+{
+  simgrid::kernel::actor::simcall_answered([this, p] { max_power_w_ = p; });
+  return this;
+}
+
+Photovoltaic* Photovoltaic::set_eval_cost(bool e)
+{
+  simgrid::kernel::actor::simcall_answered([this, e] { eval_cost_ = e; });
+  return this;
+}
+
+Photovoltaic* Photovoltaic::set_lifespan(int l)
+{
+  xbt_assert(l > 0, " : lifespan should be > 0 (provided: %d)", l);
+  simgrid::kernel::actor::simcall_answered([this, l] { lifespan_years_ = l; });
+  return this;
+}
+
+Photovoltaic* Photovoltaic::set_investment_cost(double c)
+{
+  xbt_assert(c > 0, " : investment cost should be > 0 (provided: %f)", c);
+  simgrid::kernel::actor::simcall_answered([this, c] { investment_cost_per_w_ = c; });
+  return this;
+}
+
+Photovoltaic* Photovoltaic::set_maintenance_cost(double c)
+{
+  xbt_assert(c > 0, " : maintenance cost hould be > 0 (provided: %f)", c);
+  simgrid::kernel::actor::simcall_answered([this, c] { maintenance_cost_per_wh_ = c; });
+  return this;
+}
+
+double Photovoltaic::get_power()
+{
+  update();
+  return power_w_;
+}
+
+simgrid::xbt::Extension<simgrid::s4u::Host, Photovoltaic> Photovoltaic::EXTENSION_ID;
+
+void Photovoltaic::init_photovoltaic_params()
+{
+  const char* prop_chars;
+  prop_chars = host_->get_property("photovoltaic_area");
+  if (prop_chars)
+    set_area(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
+  prop_chars = host_->get_property("photovoltaic_conversion_efficiency");
+  if (prop_chars)
+    set_conversion_efficiency(
+        xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
+  prop_chars = host_->get_property("photovoltaic_solar_irradiance");
+  if (prop_chars)
+    set_solar_irradiance(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
+  prop_chars = host_->get_property("photovoltaic_min_power");
+  if (prop_chars)
+    set_min_power(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
+  prop_chars = host_->get_property("photovoltaic_max_power");
+  if (prop_chars)
+    set_max_power(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
+  prop_chars = host_->get_property("photovoltaic_eval_cost");
+  if (prop_chars)
+    set_eval_cost(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str()));
+  prop_chars = host_->get_property("photovoltaic_lifespan");
+  if (prop_chars)
+    set_lifespan(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str()));
+  prop_chars = host_->get_property("photovoltaic_investment_cost");
+  if (prop_chars)
+    set_investment_cost(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
+  prop_chars = host_->get_property("photovoltaic_maintenance_cost");
+  if (prop_chars)
+    set_maintenance_cost(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
+  simgrid::kernel::actor::simcall_answered([this] { last_updated_ = simgrid::s4u::Engine::get_clock(); });
+}
+
+void Photovoltaic::update()
+{
+  simgrid::kernel::actor::simcall_answered([this] {
+    double now = simgrid::s4u::Engine::get_clock();
+    if (now <= last_updated_)
+      return;
+    double power_w = conversion_efficiency_ * area_m2_ * solar_irradiance_w_per_m2_;
+    if (min_power_w_ > 0 and power_w_ < min_power_w_)
+      power_w = 0;
+    if (max_power_w_ > 0 and power_w_ > max_power_w_)
+      power_w = max_power_w_;
+    power_w_ = power_w;
+    if (eval_cost_) {
+      xbt_assert(max_power_w_ > 0, " : max power must be > 0 (provided: %f)", max_power_w_);
+      cumulative_cost_ += max_power_w_ * (now - last_updated_) *
+                          (investment_cost_per_w_ / (lifespan_years_ * 8760 * 3600) + maintenance_cost_per_wh_ / 3600);
+    }
+    last_updated_ = now;
+  });
+}
+
+Photovoltaic::Photovoltaic(simgrid::s4u::Host* host) : host_(host)
+{
+  init_photovoltaic_params();
+}
+
+Photovoltaic::~Photovoltaic() = default;
+} // namespace simgrid::plugin
+
+using simgrid::plugin::Photovoltaic;
+
+/* **************************** events  callback *************************** */
+
+static void on_creation(simgrid::s4u::Host& host)
+{
+  if (dynamic_cast<simgrid::s4u::VirtualMachine*>(&host)) // Ignore virtual machines
+    return;
+  host.extension_set(new Photovoltaic(&host));
+}
+
+/* **************************** Public interface *************************** */
+
+static void ensure_plugin_inited()
+{
+  if (not Photovoltaic::EXTENSION_ID.valid())
+    throw simgrid::xbt::InitializationError(
+        "The Photovoltaic plugin is not active. Please call sg_photovoltaic_plugin_init() "
+        "before calling any function related to that plugin.");
+}
+
+/** @ingroup plugin_photovoltaic
+ *  @brief Enable photovoltaic plugin.
+ */
+void sg_photovoltaic_plugin_init()
+{
+  if (Photovoltaic::EXTENSION_ID.valid())
+    return;
+  Photovoltaic::EXTENSION_ID = simgrid::s4u::Host::extension_create<Photovoltaic>();
+  simgrid::s4u::Host::on_creation_cb(&on_creation);
+}
+
+/** @ingroup plugin_photovoltaic
+ *  @param s The solar irradiance to set in W/m².
+ */
+void sg_photovoltaic_set_solar_irradiance(const_sg_host_t host, double s)
+{
+  ensure_plugin_inited();
+  host->extension<Photovoltaic>()->set_solar_irradiance(s);
+}
+
+/** @ingroup plugin_photovoltaic
+ *  @return Power generation in W.
+ */
+double sg_photovoltaic_get_power(const_sg_host_t host)
+{
+  ensure_plugin_inited();
+  return host->extension<Photovoltaic>()->get_power();
+}
\ No newline at end of file
diff --git a/src/plugins/task.cpp b/src/plugins/task.cpp
new file mode 100644 (file)
index 0000000..f0bf5b1
--- /dev/null
@@ -0,0 +1,424 @@
+#include <simgrid/Exception.hpp>
+#include <simgrid/plugins/task.hpp>
+#include <simgrid/s4u/Comm.hpp>
+#include <simgrid/s4u/Exec.hpp>
+#include <simgrid/s4u/Io.hpp>
+#include <simgrid/simix.hpp>
+
+#include "src/simgrid/module.hpp"
+
+SIMGRID_REGISTER_PLUGIN(task, "Battery management", nullptr)
+/** @defgroup plugin_task plugin_task Plugin Task
+
+  @beginrst
+
+This is the task plugin, enabling management of Tasks.
+To activate this plugin, first call :cpp:func:`Task::init`.
+
+Tasks are designed to represent dataflows, i.e, graphs of Tasks.
+Tasks can only be instancied using either
+:cpp:func:`simgrid::plugins::ExecTask::init` or :cpp:func:`simgrid::plugins::CommTask::init`
+An ExecTask is an Execution Task. Its underlying Activity is an :ref:`Exec <API_s4u_Exec>`.
+A CommTask is a Communication Task. Its underlying Activity is a :ref:`Comm <API_s4u_Comm>`.
+
+  @endrst
+ */
+XBT_LOG_NEW_DEFAULT_SUBCATEGORY(Task, kernel, "Logging specific to the task plugin");
+
+namespace simgrid::plugins {
+
+xbt::signal<void(Task*)> Task::on_start;
+xbt::signal<void(Task*)> Task::on_end;
+
+Task::Task(const std::string& name) : name_(name) {}
+
+/**
+ *  @param predecessor The Task to add.
+ *  @brief Add a predecessor to this Task.
+ */
+void Task::add_predecessor(Task* predecessor)
+{
+  if (predecessors_.find(predecessor) == predecessors_.end())
+    simgrid::kernel::actor::simcall_answered([this, predecessor] { predecessors_[predecessor] = 0; });
+}
+
+/**
+ *  @param predecessor The Task to remove.
+ *  @brief Remove a predecessor from this Task.
+ */
+void Task::remove_predecessor(Task* predecessor)
+{
+  simgrid::kernel::actor::simcall_answered([this, predecessor] { predecessors_.erase(predecessor); });
+}
+
+/**
+ *  @brief Return True if the Task can start a new Activity.
+ *  @note The Task is ready if not already doing something and there is at least one execution waiting in queue.
+ */
+bool Task::ready_to_run() const
+{
+  return not working_ && queued_execs_ > 0;
+}
+
+/**
+ *  @param source The sender.
+ *  @brief Receive a token from another Task.
+ *  @note Check upon reception if the Task has received a token from each of its predecessors,
+ * and in this case consumes those tokens and enqueue an execution.
+ */
+void Task::receive(Task* source)
+{
+  XBT_DEBUG("Task %s received a token from %s", name_.c_str(), source->name_.c_str());
+  auto it = predecessors_.find(source);
+  simgrid::kernel::actor::simcall_answered([this, it] {
+    it->second++;
+    bool enough_tokens = true;
+    for (auto const& [key, val] : predecessors_)
+      if (val < 1) {
+        enough_tokens = false;
+        break;
+      }
+    if (enough_tokens) {
+      for (auto& [key, val] : predecessors_)
+        val--;
+      enqueue_execs(1);
+    }
+  });
+}
+
+/**
+ *  @brief Task routine when finishing an execution.
+ *  @note Set its working status as false.
+ * Add 1 to its count of finished executions.
+ * Call the on_this_end func.
+ * Fire on_end callback.
+ * Send a token to each of its successors.
+ * Start a new execution if possible.
+ */
+void Task::complete()
+{
+  simgrid::kernel::actor::simcall_answered([this] {
+    working_ = false;
+    count_++;
+  });
+  for (auto end_func : end_func_handlers_)
+    end_func(this);
+  Task::on_end(this);
+  for (auto const& t : successors_)
+    t->receive(this);
+  if (ready_to_run())
+    fire();
+}
+
+/** @ingroup plugin_task
+ *  @brief Init the Task plugin.
+ *  @note Add a completion callback to all Activities to call Task::complete().
+ */
+void Task::init()
+{
+  if (Task::inited_)
+    return;
+  Task::inited_                           = true;
+  ExtendedAttributeActivity::EXTENSION_ID = simgrid::s4u::Activity::extension_create<ExtendedAttributeActivity>();
+  simgrid::s4u::Exec::on_completion_cb(
+      [](simgrid::s4u::Exec const& exec) { exec.extension<ExtendedAttributeActivity>()->task_->complete(); });
+  simgrid::s4u::Comm::on_completion_cb(
+      [](simgrid::s4u::Comm const& comm) { comm.extension<ExtendedAttributeActivity>()->task_->complete(); });
+  simgrid::s4u::Io::on_completion_cb(
+      [](simgrid::s4u::Io const& io) { io.extension<ExtendedAttributeActivity>()->task_->complete(); });
+}
+
+/** @ingroup plugin_task
+ *  @param n The number of executions to enqueue.
+ *  @brief Enqueue executions.
+ *  @note Immediatly starts an execution if possible.
+ */
+void Task::enqueue_execs(int n)
+{
+  simgrid::kernel::actor::simcall_answered([this, n] {
+    queued_execs_ += n;
+    if (ready_to_run())
+      fire();
+  });
+}
+
+/** @ingroup plugin_task
+ *  @param amount The amount to set.
+ *  @brief Set the amout of work to do.
+ *  @note Amount in flop for ExecTask and in bytes for CommTask.
+ */
+void Task::set_amount(double amount)
+{
+  simgrid::kernel::actor::simcall_answered([this, amount] { amount_ = amount; });
+}
+
+/** @ingroup plugin_task
+ *  @param successor The Task to add.
+ *  @brief Add a successor to this Task.
+ *  @note It also adds this as a predecessor of successor.
+ */
+void Task::add_successor(TaskPtr successor)
+{
+  simgrid::kernel::actor::simcall_answered([this, successor] { successors_.insert(successor.get()); });
+  successor->add_predecessor(this);
+}
+
+/** @ingroup plugin_task
+ *  @param successor The Task to remove.
+ *  @brief Remove a successor from this Task.
+ *  @note It also remove this from the predecessors of successor.
+ */
+void Task::remove_successor(TaskPtr successor)
+{
+  simgrid::kernel::actor::simcall_answered([this, successor] { successors_.erase(successor.get()); });
+  successor->remove_predecessor(this);
+}
+
+void Task::remove_all_successors()
+{
+  simgrid::kernel::actor::simcall_answered([this] {
+    while (not successors_.empty()) {
+      auto* successor = *(successors_.begin());
+      successor->predecessors_.erase(this);
+      successors_.erase(successor);
+    }
+  });
+}
+
+/** @ingroup plugin_task
+ *  @param func The function to set.
+ *  @brief Set a function to be called before each execution.
+ *  @note The function is called before the underlying Activity starts.
+ */
+void Task::on_this_start(const std::function<void(Task*)>& func)
+{
+  simgrid::kernel::actor::simcall_answered([this, &func] { start_func_handlers_.push_back(func); });
+}
+
+/** @ingroup plugin_task
+ *  @param func The function to set.
+ *  @brief Set a function to be called after each execution.
+ *  @note The function is called after the underlying Activity ends, but before sending tokens to successors.
+ */
+void Task::on_this_end(const std::function<void(Task*)>& func)
+{
+  simgrid::kernel::actor::simcall_answered([this, &func] { end_func_handlers_.push_back(func); });
+}
+
+/** @ingroup plugin_task
+ *  @brief Return the number of completed executions.
+ */
+int Task::get_count() const
+{
+  return count_;
+}
+
+/**
+ *  @brief Default constructor.
+ */
+ExecTask::ExecTask(const std::string& name) : Task(name) {}
+
+/** @ingroup plugin_task
+ *  @brief Smart Constructor.
+ */
+ExecTaskPtr ExecTask::init(const std::string& name)
+{
+  return ExecTaskPtr(new ExecTask(name));
+}
+
+/** @ingroup plugin_task
+ *  @brief Smart Constructor.
+ */
+ExecTaskPtr ExecTask::init(const std::string& name, double flops, s4u::Host* host)
+{
+  return init(name)->set_flops(flops)->set_host(host);
+}
+
+/**
+ *  @brief Do one execution of the Task.
+ *  @note Call the on_this_start() func. Set its working status as true.
+ *  Init and start the underlying Activity.
+ */
+void ExecTask::fire()
+{
+  for (auto start_func : start_func_handlers_)
+    start_func(this);
+  Task::on_start(this);
+  kernel::actor::simcall_answered([this] {
+    working_      = true;
+    queued_execs_ = std::max(queued_execs_ - 1, 0);
+  });
+  s4u::ExecPtr exec = s4u::Exec::init();
+  exec->set_name(name_);
+  exec->set_flops_amount(amount_);
+  exec->set_host(host_);
+  exec->start();
+  exec->extension_set(new ExtendedAttributeActivity());
+  exec->extension<ExtendedAttributeActivity>()->task_ = this;
+  kernel::actor::simcall_answered([this, exec] { current_activity_ = exec; });
+}
+
+/** @ingroup plugin_task
+ *  @param host The host to set.
+ *  @brief Set a new host.
+ */
+ExecTaskPtr ExecTask::set_host(s4u::Host* host)
+{
+  kernel::actor::simcall_answered([this, host] { host_ = host; });
+  return this;
+}
+
+/** @ingroup plugin_task
+ *  @param flops The amount of flops to set.
+ */
+ExecTaskPtr ExecTask::set_flops(double flops)
+{
+  kernel::actor::simcall_answered([this, flops] { amount_ = flops; });
+  return this;
+}
+
+/**
+ *  @brief Default constructor.
+ */
+CommTask::CommTask(const std::string& name) : Task(name) {}
+
+/** @ingroup plugin_task
+ *  @brief Smart constructor.
+ */
+CommTaskPtr CommTask::init(const std::string& name)
+{
+  return CommTaskPtr(new CommTask(name));
+}
+
+/** @ingroup plugin_task
+ *  @brief Smart constructor.
+ */
+CommTaskPtr CommTask::init(const std::string& name, double bytes, s4u::Host* source, s4u::Host* destination)
+{
+  return init(name)->set_bytes(bytes)->set_source(source)->set_destination(destination);
+}
+
+/**
+ *  @brief Do one execution of the Task.
+ *  @note Call the on_this_start() func. Set its working status as true.
+ *  Init and start the underlying Activity.
+ */
+void CommTask::fire()
+{
+  for (auto start_func : start_func_handlers_)
+    start_func(this);
+  Task::on_start(this);
+  kernel::actor::simcall_answered([this] {
+    working_      = true;
+    queued_execs_ = std::max(queued_execs_ - 1, 0);
+  });
+  s4u::CommPtr comm = s4u::Comm::sendto_init(source_, destination_);
+  comm->set_name(name_);
+  comm->set_payload_size(amount_);
+  comm->start();
+  comm->extension_set(new ExtendedAttributeActivity());
+  comm->extension<ExtendedAttributeActivity>()->task_ = this;
+  kernel::actor::simcall_answered([this, comm] { current_activity_ = comm; });
+}
+
+/** @ingroup plugin_task
+ *  @param source The host to set.
+ *  @brief Set a new source host.
+ */
+CommTaskPtr CommTask::set_source(s4u::Host* source)
+{
+  kernel::actor::simcall_answered([this, source] { source_ = source; });
+  return this;
+}
+
+/** @ingroup plugin_task
+ *  @param destination The host to set.
+ *  @brief Set a new destination host.
+ */
+CommTaskPtr CommTask::set_destination(s4u::Host* destination)
+{
+  kernel::actor::simcall_answered([this, destination] { destination_ = destination; });
+  return this;
+}
+
+/** @ingroup plugin_task
+ *  @param bytes The amount of bytes to set.
+ */
+CommTaskPtr CommTask::set_bytes(double bytes)
+{
+  kernel::actor::simcall_answered([this, bytes] { amount_ = bytes; });
+  return this;
+}
+
+/**
+ *  @brief Default constructor.
+ */
+IoTask::IoTask(const std::string& name) : Task(name) {}
+
+/** @ingroup plugin_task
+ *  @brief Smart Constructor.
+ */
+IoTaskPtr IoTask::init(const std::string& name)
+{
+  return IoTaskPtr(new IoTask(name));
+}
+
+/** @ingroup plugin_task
+ *  @brief Smart Constructor.
+ */
+IoTaskPtr IoTask::init(const std::string& name, double bytes, s4u::Disk* disk, s4u::Io::OpType type)
+{
+  return init(name)->set_bytes(bytes)->set_disk(disk)->set_op_type(type);
+}
+
+/** @ingroup plugin_task
+ *  @param disk The disk to set.
+ *  @brief Set a new disk.
+ */
+IoTaskPtr IoTask::set_disk(s4u::Disk* disk)
+{
+  kernel::actor::simcall_answered([this, disk] { disk_ = disk; });
+  return this;
+}
+
+/** @ingroup plugin_task
+ *  @param bytes The amount of bytes to set.
+ */
+IoTaskPtr IoTask::set_bytes(double bytes)
+{
+  kernel::actor::simcall_answered([this, bytes] { amount_ = bytes; });
+  return this;
+}
+
+/** @ingroup plugin_task */
+IoTaskPtr IoTask::set_op_type(s4u::Io::OpType type)
+{
+  kernel::actor::simcall_answered([this, type] { type_ = type; });
+  return this;
+}
+
+void IoTask::fire()
+{
+  for (auto start_func : start_func_handlers_)
+    start_func(this);
+  Task::on_start(this);
+  kernel::actor::simcall_answered([this] {
+    working_      = true;
+    queued_execs_ = std::max(queued_execs_ - 1, 0);
+  });
+  s4u::IoPtr io = s4u::Io::init();
+  io->set_name(name_);
+  io->set_size(amount_);
+  io->set_disk(disk_);
+  io->set_op_type(type_);
+  io->start();
+  io->extension_set(new ExtendedAttributeActivity());
+  io->extension<ExtendedAttributeActivity>()->task_ = this;
+  kernel::actor::simcall_answered([this, io] { current_activity_ = io; });
+}
+
+} // namespace simgrid::plugins
+
+simgrid::xbt::Extension<simgrid::s4u::Activity, simgrid::plugins::ExtendedAttributeActivity>
+    simgrid::plugins::ExtendedAttributeActivity::EXTENSION_ID;
+bool simgrid::plugins::Task::inited_ = false;
index d5a7443..8160068 100644 (file)
@@ -88,9 +88,9 @@ static void on_exec_creation(simgrid::s4u::Exec const& e)
   }
 }
 
-static void on_exec_completion(const simgrid::s4u::Activity& e)
+static void on_exec_completion(const simgrid::s4u::Exec& e)
 {
-  const auto exec = dynamic_cast<simgrid::kernel::activity::ExecImpl*>(e.get_impl());
+  const auto* exec = dynamic_cast<simgrid::kernel::activity::ExecImpl*>(e.get_impl());
   if (exec == nullptr)
     return;
   const simgrid::s4u::VirtualMachine* vm = dynamic_cast<simgrid::s4u::VirtualMachine*>(exec->get_host());
@@ -113,7 +113,7 @@ void sg_vm_dirty_page_tracking_init()
         simgrid::kernel::resource::VirtualMachineImpl::extension_create<DirtyPageTrackingExt>();
     simgrid::s4u::VirtualMachine::on_creation_cb(&on_virtual_machine_creation);
     simgrid::s4u::Exec::on_start_cb(&on_exec_creation);
-    simgrid::s4u::Activity::on_completion_cb(&on_exec_completion);
+    simgrid::s4u::Exec::on_completion_cb(&on_exec_completion);
   }
 }
 
index 7b38f61..aa352f1 100644 (file)
@@ -24,11 +24,6 @@ template class xbt::Extendable<s4u::Activity>;
 
 namespace s4u {
 
-xbt::signal<void(Activity&)> Activity::on_veto;
-xbt::signal<void(Activity const&)> Activity::on_completion;
-xbt::signal<void(Activity const&)> Activity::on_suspended;
-xbt::signal<void(Activity const&)> Activity::on_resumed;
-
 std::set<Activity*>* Activity::vetoed_activities_ = nullptr;
 
 void Activity::destroy()
index 3e84903..e60f73d 100644 (file)
@@ -164,6 +164,7 @@ void Actor::set_host(Host* new_host)
   });
 
   s4u::Actor::on_host_change(*this, *previous_location);
+  s4u::Actor::on_this_host_change(*this, *previous_location);
 }
 
 s4u::Host* Actor::get_host() const
@@ -213,6 +214,7 @@ void Actor::suspend()
   kernel::actor::ActorImpl* issuer = kernel::actor::ActorImpl::self();
   kernel::actor::ActorImpl* target = pimpl_;
   s4u::Actor::on_suspend(*this);
+  s4u::Actor::on_this_suspend(*this);
   kernel::actor::simcall_blocking([issuer, target]() {
     target->suspend();
     if (target != issuer) {
@@ -226,6 +228,7 @@ void Actor::resume()
 {
   kernel::actor::simcall_answered([this] { pimpl_->resume(); });
   s4u::Actor::on_resume(*this);
+  s4u::Actor::on_this_resume(*this);
 }
 
 bool Actor::is_suspended() const
@@ -326,6 +329,7 @@ void sleep_for(double duration)
 
   kernel::actor::ActorImpl* issuer = kernel::actor::ActorImpl::self();
   Actor::on_sleep(*issuer->get_ciface());
+  issuer->get_ciface()->on_this_sleep(*issuer->get_ciface());
 
   kernel::actor::simcall_blocking([issuer, duration]() {
     if (MC_is_active() || MC_record_replay_is_active()) {
@@ -338,6 +342,7 @@ void sleep_for(double duration)
   });
 
   Actor::on_wake_up(*issuer->get_ciface());
+  issuer->get_ciface()->on_this_wake_up(*issuer->get_ciface());
 }
 
 void yield()
@@ -442,6 +447,7 @@ void suspend()
 {
   kernel::actor::ActorImpl* self = simgrid::kernel::actor::ActorImpl::self();
   s4u::Actor::on_suspend(*self->get_ciface());
+  self->get_ciface()->on_this_suspend(*self->get_ciface());
   kernel::actor::simcall_blocking([self] { self->suspend(); });
 }
 
index 6ec1ecd..f7bfc9e 100644 (file)
@@ -58,6 +58,8 @@ Comm::~Comm()
       XBT_INFO("pimpl_ is null");
     xbt_backtrace_display_current();
   }
+  if (pimpl_ != nullptr)
+    pimpl_->set_iface(nullptr);
 }
 
 void Comm::send(kernel::actor::ActorImpl* sender, const Mailbox* mbox, double task_size, double rate, void* src_buff,
@@ -289,6 +291,14 @@ Actor* Comm::get_sender() const
   return sender ? sender->get_ciface() : nullptr;
 }
 
+Actor* Comm::get_receiver() const
+{
+  kernel::actor::ActorImplPtr receiver = nullptr;
+  if (pimpl_)
+    receiver = boost::static_pointer_cast<kernel::activity::CommImpl>(pimpl_)->dst_actor_;
+  return receiver ? receiver->get_ciface() : nullptr;
+}
+
 bool Comm::is_assigned() const
 {
   return (pimpl_ && boost::static_pointer_cast<kernel::activity::CommImpl>(pimpl_)->is_assigned()) ||
@@ -304,12 +314,15 @@ Comm* Comm::do_start()
     xbt_assert(src_buff_ == nullptr && dst_buff_ == nullptr,
                "Direct host-to-host communications cannot carry any data.");
     XBT_DEBUG("host-to-host Comm. Pimpl already created and set, just start it.");
+    on_start(*this);
+    on_this_start(*this);
     kernel::actor::simcall_answered([this] {
       pimpl_->set_state(kernel::activity::State::READY);
       boost::static_pointer_cast<kernel::activity::CommImpl>(pimpl_)->start();
     });
   } else if (src_buff_ != nullptr) { // Sender side
     on_send(*this);
+    on_this_send(*this);
     kernel::actor::CommIsendSimcall observer{sender_,
                                              mailbox_->get_impl(),
                                              remains_,
@@ -326,6 +339,7 @@ Comm* Comm::do_start()
   } else if (dst_buff_ != nullptr) { // Receiver side
     xbt_assert(not detached_, "Receive cannot be detached");
     on_recv(*this);
+    on_this_recv(*this);
     kernel::actor::CommIrecvSimcall observer{receiver_,
                                              mailbox_->get_impl(),
                                              static_cast<unsigned char*>(dst_buff_),
@@ -346,6 +360,11 @@ Comm* Comm::do_start()
   if (not detached_) {
     pimpl_->set_iface(this);
     pimpl_->set_actor(sender_);
+    // Only throw the signal when both sides are here and the status is READY
+    if (pimpl_->get_state() != kernel::activity::State::WAITING) {
+      on_start(*this);
+      on_this_start(*this);
+    }
   }
 
   state_ = State::STARTED;
@@ -391,11 +410,13 @@ Comm* Comm::wait_for(double timeout)
         return start()->wait_for(timeout); // In the case of host2host comm, do it in two simcalls
       } else if (src_buff_ != nullptr) {
         on_send(*this);
+        on_this_send(*this);
         send(sender_, mailbox_, remains_, rate_, src_buff_, src_buff_size_, match_fun_, copy_data_function_,
              get_data<void>(), timeout);
 
       } else { // Receiver
         on_recv(*this);
+        on_this_recv(*this);
         recv(receiver_, mailbox_, dst_buff_, &dst_buff_size_, match_fun_, copy_data_function_, get_data<void>(),
              timeout, rate_);
       }
index 0fa8295..c97f72b 100644 (file)
@@ -17,7 +17,7 @@ namespace s4u {
 
 xbt::signal<void(Disk&)> Disk::on_creation;
 xbt::signal<void(Disk const&)> Disk::on_destruction;
-xbt::signal<void(Disk const&)> Disk::on_state_change;
+xbt::signal<void(Disk const&)> Disk::on_onoff;
 
 const std::string& Disk::get_name() const
 {
index 3a87ef2..a6fd9cb 100644 (file)
@@ -16,7 +16,6 @@
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(s4u_exec, s4u_activity, "S4U asynchronous executions");
 
 namespace simgrid::s4u {
-xbt::signal<void(Exec const&)> Exec::on_start;
 
 Exec::Exec(kernel::activity::ExecImplPtr pimpl)
 {
@@ -48,6 +47,7 @@ Exec* Exec::do_start()
 
   state_      = State::STARTED;
   on_start(*this);
+  on_this_start(*this);
   return this;
 }
 
index 7aa931a..91da353 100644 (file)
@@ -32,8 +32,9 @@ namespace s4u {
 #ifndef DOXYGEN
 xbt::signal<void(Host&)> Host::on_creation;
 xbt::signal<void(Host const&)> Host::on_destruction;
-xbt::signal<void(Host const&)> Host::on_state_change;
+xbt::signal<void(Host const&)> Host::on_onoff;
 xbt::signal<void(Host const&)> Host::on_speed_change;
+xbt::signal<void(kernel::resource::CpuAction&, kernel::resource::Action::State)> Host::on_exec_state_change;
 #endif
 
 Host* Host::set_cpu(kernel::resource::CpuImpl* cpu)
@@ -101,7 +102,8 @@ void Host::turn_on()
     kernel::actor::simcall_answered([this] {
       this->pimpl_cpu_->turn_on();
       this->pimpl_->turn_on();
-      on_state_change(*this);
+      on_onoff(*this);
+      on_this_onoff(*this);
     });
   }
 }
@@ -115,7 +117,8 @@ void Host::turn_off()
       this->pimpl_cpu_->turn_off();
       this->pimpl_->turn_off(self);
 
-      on_state_change(*this);
+      on_onoff(*this);
+      on_this_onoff(*this);
     });
   }
 }
index 191abc1..691be4a 100644 (file)
@@ -14,7 +14,6 @@
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(s4u_io, s4u_activity, "S4U asynchronous I/Os");
 
 namespace simgrid::s4u {
-xbt::signal<void(Io const&)> Io::on_start;
 
 Io::Io(kernel::activity::IoImplPtr pimpl)
 {
@@ -91,6 +90,7 @@ Io* Io::do_start()
 
   state_ = State::STARTED;
   on_start(*this);
+  on_this_start(*this);
   return this;
 }
 
index 4280066..0a42518 100644 (file)
@@ -22,7 +22,7 @@ namespace s4u {
 
 xbt::signal<void(Link&)> Link::on_creation;
 xbt::signal<void(Link const&)> Link::on_destruction;
-xbt::signal<void(Link const&)> Link::on_state_change;
+xbt::signal<void(Link const&)> Link::on_onoff;
 xbt::signal<void(Link const&)> Link::on_bandwidth_change;
 xbt::signal<void(kernel::resource::NetworkAction&, kernel::resource::Action::State)>
     Link::on_communication_state_change;
index 1c305be..9f6f771 100644 (file)
@@ -74,6 +74,7 @@ void VirtualMachine::destroy()
 
     XBT_DEBUG("destroy %s", get_cname());
     on_vm_destruction(*this);
+    on_this_vm_destruction(*this);
     /* Then, destroy the VM object */
     kernel::actor::simcall_answered(
         [this]() { get_vm_impl()->get_physical_host()->get_impl()->destroy_vm(get_name()); });
index f1b6524..e46d67e 100644 (file)
@@ -6,7 +6,6 @@
 #ifndef SMPI_TOPO_HPP_INCLUDED
 #define SMPI_TOPO_HPP_INCLUDED
 
-#include "smpi_comm.hpp"
 #include "smpi_status.hpp"
 #include <memory>
 
index 93ca639..c743864 100644 (file)
@@ -39,7 +39,7 @@ template <class T> inline void hash_combine(std::size_t& seed, T const& v)
 }
 
 // Recursive template code derived from Matthieu M.
-template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1> class HashValueImpl {
+template <class Tuple, size_t Index = std::tuple_size_v<Tuple> - 1> class HashValueImpl {
 public:
   static void apply(size_t& seed, Tuple const& tuple)
   {
index bdec55b..a1e6be7 100644 (file)
@@ -25,7 +25,7 @@ F2C::F2C() = default;
 int F2C::add_f()
 {
   allocate_lookup();
-  if (auto loc = smpi_process()->call_location(); loc && loc->linenumber != 0)
+  if (auto const* loc = smpi_process()->call_location(); loc && loc->linenumber != 0)
     call_location_= std::string (loc->filename + ":" + std::to_string(loc->linenumber));
   my_f2c_id_                 = global_f2c_id();
   (*f2c_lookup_)[my_f2c_id_] = this;
index 3843601..1723741 100644 (file)
@@ -413,7 +413,7 @@ int main(int argc, char* argv[])
                   {sg4::LinkInRoute{link}}, false);
   zone->seal();
 
-  sg4::Host::on_state_change_cb([mbox](sg4::Host const& host) {
+  sg4::Host::on_onoff_cb([mbox](sg4::Host const& host) {
     XBT_DEBUG("Host %s is now %s", host.get_cname(), host.is_on() ? "ON " : "OFF");
     if (not host.is_on()) {
       mbox.eager->clear();
@@ -421,7 +421,7 @@ int main(int argc, char* argv[])
     }
   });
 
-  sg4::Link::on_state_change_cb(
+  sg4::Link::on_onoff_cb(
       [](sg4::Link const& lnk) { XBT_DEBUG("Link %s is now %s", lnk.get_cname(), lnk.is_on() ? "ON " : "OFF"); });
 
   e.run_until(end_time);
index b229d70..fa1e49d 100644 (file)
@@ -13,12 +13,9 @@ int main(int argc, char** argv)
   xbt_assert(argc > 1, "Usage: %s platform_file\n\nExample: %s two_clusters.xml", argv[0], argv[0]);
   engine.load_platform(argv[1]);
 
-  simgrid::s4u::Activity::on_completion_cb([](simgrid::s4u::Activity const& activity) {
-    const auto* exec = dynamic_cast<simgrid::s4u::Exec const*>(&activity);
-    if (exec == nullptr) // Only Execs are concerned here
-      return;
-    XBT_INFO("Exec '%s' start time: %f, finish time: %f", exec->get_cname(), exec->get_start_time(),
-             exec->get_finish_time());
+  simgrid::s4u::Exec::on_completion_cb([](simgrid::s4u::Exec const& exec) {
+    XBT_INFO("Exec '%s' start time: %f, finish time: %f", exec.get_cname(), exec.get_start_time(),
+             exec.get_finish_time());
   });
 
   /* creation of the activities and their dependencies */
index 9439fc2..351c4e4 100644 (file)
@@ -86,8 +86,8 @@ static void get_set_disk_data(simgrid::s4u::Disk* disk)
 
 static void dump_platform_disks()
 {
-  for (auto const& h : simgrid::s4u::Engine::get_instance()->get_all_hosts())
-    for (auto const& d : h->get_disks()) {
+  for (auto const* h : simgrid::s4u::Engine::get_instance()->get_all_hosts())
+    for (auto* d : h->get_disks()) {
       if (h == d->get_host())
         XBT_INFO("%s is attached to %s", d->get_cname(), d->get_host()->get_cname());
       d->set_property("other usage", "gpfs");
@@ -98,7 +98,7 @@ static void disk_info(const simgrid::s4u::Host* host)
 {
   XBT_INFO("*** Disk info on %s ***", host->get_cname());
 
-  for (auto const& disk : host->get_disks()) {
+  for (auto const* disk : host->get_disks()) {
     const char* mount_name = sg_disk_get_mount_point(disk);
     XBT_INFO("  Disk name: %s, mount name: %s", disk->get_cname(), mount_name);
 
index 733d9a5..69cdf86 100644 (file)
@@ -27,14 +27,14 @@ static void print_status(const std::vector<simgrid::s4u::Host*>& hosts)
 {
   XBT_INFO("---- HOSTS and VMS STATUS ----");
   XBT_INFO("--- HOSTS ---");
-  for (auto const& host : hosts) {
+  for (auto const* host : hosts) {
     XBT_INFO("+ Name:%s Load:%f", host->get_cname(), host->get_load());
     for (auto const& actor : host->get_all_actors())
       XBT_INFO("++ actor: %s", actor->get_cname());
   }
   XBT_INFO("--- VMS ---");
-  for (auto const& host : simgrid::s4u::Engine::get_instance()->get_all_hosts()) {
-    if (auto vm = dynamic_cast<simgrid::s4u::VirtualMachine*>(host)) {
+  for (auto const* host : simgrid::s4u::Engine::get_instance()->get_all_hosts()) {
+    if (auto const* vm = dynamic_cast<const simgrid::s4u::VirtualMachine*>(host)) {
       XBT_INFO("+ Name:%s Host:%s Load:%f State: %s", vm->get_cname(), vm->get_pm()->get_cname(), vm->get_load(),
                simgrid::s4u::VirtualMachine::to_c_str(vm->get_state()));
       for (auto const& actor : host->get_all_actors())
index 96f475d..5377503 100644 (file)
@@ -403,4 +403,246 @@ $ $VALGRIND_NO_LEAK_CHECK ${bindir:=.}/../../../smpi_script/bin/smpirun -wrapper
 > If this is too much, consider sharing allocations for computation buffers.
 > This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
 > 
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 635 unique states visited; 173 backtracks (3896 transition replays, 3088 states visited overall)
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+> 
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1005 unique states visited; 276 backtracks (6560 transition replays, 5279 states visited overall)
index 02b1cdf..a0c1676 100644 (file)
@@ -11,29 +11,29 @@ static void worker()
 {
   simgrid::s4u::Host* other_host = simgrid::s4u::Host::by_name("Fafard");
   unsigned int first =
-      simgrid::s4u::Host::on_state_change.connect([](simgrid::s4u::Host const&) { XBT_INFO("First callback"); });
+      simgrid::s4u::Host::on_onoff.connect([](simgrid::s4u::Host const&) { XBT_INFO("First callback"); });
   unsigned int second =
-      simgrid::s4u::Host::on_state_change.connect([](simgrid::s4u::Host const&) { XBT_INFO("Second callback"); });
+      simgrid::s4u::Host::on_onoff.connect([](simgrid::s4u::Host const&) { XBT_INFO("Second callback"); });
   unsigned int third =
-      simgrid::s4u::Host::on_state_change.connect([](simgrid::s4u::Host const&) { XBT_INFO("Third callback"); });
+      simgrid::s4u::Host::on_onoff.connect([](simgrid::s4u::Host const&) { XBT_INFO("Third callback"); });
 
   XBT_INFO("Turning off: Three callbacks should be triggered");
   other_host->turn_off();
 
   XBT_INFO("Disconnect the second callback");
-  simgrid::s4u::Host::on_state_change.disconnect(second);
+  simgrid::s4u::Host::on_onoff.disconnect(second);
 
   XBT_INFO("Turning on: Two callbacks should be triggered");
   other_host->turn_on();
 
   XBT_INFO("Disconnect the first callback");
-  simgrid::s4u::Host::on_state_change.disconnect(first);
+  simgrid::s4u::Host::on_onoff.disconnect(first);
 
   XBT_INFO("Turning off: One callback should be triggered");
   other_host->turn_off();
 
   XBT_INFO("Disconnect the third callback");
-  simgrid::s4u::Host::on_state_change.disconnect(third);
+  simgrid::s4u::Host::on_onoff.disconnect(third);
   XBT_INFO("Turning on: No more callbacks");
   other_host->turn_on();
 }
index e7ca165..f478e89 100644 (file)
@@ -25,3 +25,16 @@ odr_violation:^stored_vtable$
 odr_violation:^cfg_bmf_max_iteration$
 # size=40 'cfg_bmf_precision' ../src/kernel/lmm/bmf.hpp:80:47
 odr_violation:^cfg_bmf_precision$
+
+# size=56 'on_completion' ../include/simgrid/s4u/Activity.hpp:235:55
+odr_violation:^on_completion$
+# size=56 'on_veto' ../include/simgrid/s4u/Activity.hpp:236:49
+odr_violation:^on_veto$
+# size=56 'on_suspend' ../include/simgrid/s4u/Activity.hpp:237:55
+odr_violation:^on_suspend$
+# size=56 'on_resume' ../include/simgrid/s4u/Activity.hpp:238:55
+odr_violation:^on_resume$
+
+# size=56 'on_start' ../include/simgrid/s4u/Comm.hpp:45:48
+# size=56 'on_start' ../include/simgrid/s4u/Exec.hpp:45:48
+odr_violation:^on_start$
index 38a352f..28c0892 100644 (file)
@@ -454,7 +454,8 @@ set(PLUGINS_SRC
   src/plugins/vm/VmLiveMigration.hpp
   src/plugins/vm/dirty_page_tracking.cpp
   src/plugins/battery.cpp
-  src/plugins/operation.cpp
+  src/plugins/task.cpp
+  src/plugins/photovoltaic.cpp
   )
 
 
@@ -523,6 +524,8 @@ set(MC_SRC_BASE
   
 set(MC_SRC_STATELESS
   src/mc/api/ActorState.hpp
+  src/mc/api/ClockVector.cpp
+  src/mc/api/ClockVector.hpp
   src/mc/api/State.cpp
   src/mc/api/State.hpp
   src/mc/api/RemoteApp.cpp
@@ -533,6 +536,17 @@ set(MC_SRC_STATELESS
   src/mc/explo/Exploration.cpp
   src/mc/explo/Exploration.hpp
 
+  src/mc/explo/odpor/Execution.cpp
+  src/mc/explo/odpor/Execution.hpp
+  src/mc/explo/odpor/ReversibleRaceCalculator.cpp
+  src/mc/explo/odpor/ReversibleRaceCalculator.hpp
+  src/mc/explo/odpor/WakeupTree.cpp
+  src/mc/explo/odpor/WakeupTree.hpp
+  src/mc/explo/odpor/WakeupTreeIterator.cpp
+  src/mc/explo/odpor/WakeupTreeIterator.hpp
+  src/mc/explo/odpor/odpor_forward.hpp
+  src/mc/explo/odpor/odpor_tests_private.hpp
+
   src/mc/remote/AppSide.cpp
   src/mc/remote/AppSide.hpp
   src/mc/remote/Channel.cpp
@@ -647,7 +661,8 @@ set(headers_to_install
   include/simgrid/plugins/file_system.h
   include/simgrid/plugins/live_migration.h
   include/simgrid/plugins/load.h
-  include/simgrid/plugins/operation.hpp
+  include/simgrid/plugins/task.hpp
+  include/simgrid/plugins/photovoltaic.hpp
   include/simgrid/plugins/ProducerConsumer.hpp
   include/simgrid/instr.h
   include/simgrid/mailbox.h
@@ -1125,6 +1140,7 @@ set(PLATFORMS_EXAMPLES
   examples/platforms/optorsim/gridpp_grid_2004.conf
   examples/platforms/optorsim/lcg_sept2004_grid.conf
   examples/platforms/optorsim/transform_optorsim_platform.pl
+  examples/platforms/photovoltaic_platform.xml
   examples/platforms/profiles/fafard_state.profile
   examples/platforms/profiles/faulty_host.profile
   examples/platforms/profiles/ginette_state.profile
index aab3aab..09491e4 100644 (file)
@@ -132,6 +132,9 @@ set(UNIT_TESTS  src/xbt/unit-tests_main.cpp
 
 set(MC_UNIT_TESTS src/mc/sosp/Snapshot_test.cpp 
                   src/mc/sosp/PageStore_test.cpp
+                  src/mc/explo/odpor/ClockVector_test.cpp
+                  src/mc/explo/odpor/Execution_test.cpp
+                  src/mc/explo/odpor/WakeupTree_test.cpp
                   src/mc/explo/udpor/EventSet_test.cpp
                   src/mc/explo/udpor/Unfolding_test.cpp
                   src/mc/explo/udpor/UnfoldingEvent_test.cpp