Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of https://framagit.org/mwapl/simgrid
authormlaurent <mathieu.laurent@ens-rennes.fr>
Mon, 5 Jun 2023 18:56:26 +0000 (20:56 +0200)
committermlaurent <mathieu.laurent@ens-rennes.fr>
Mon, 5 Jun 2023 18:56:26 +0000 (20:56 +0200)
347 files changed:
.circleci/config.yml
.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
.gitlab-ci.yml
CMakeLists.txt
ChangeLog
FindSimGrid.cmake
MANIFEST.in
doc/doxygen/uhood_switch.doc
docs/requirements.txt
docs/source/Configuring_SimGrid.rst
docs/source/Installing_SimGrid.rst
docs/source/Models.rst
docs/source/Plugins.rst
docs/source/Start_your_own_project.rst
docs/source/Tutorial_DAG.rst
docs/source/app_s4u.rst
docs/source/tuto_disk/CMakeLists.txt
docs/source/tuto_network_calibration/CMakeLists.txt
examples/c/platform-failures/platform-failures.tesh
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-bugged1-liveness/s4u-mc-bugged1-liveness-visited.tesh
examples/cpp/mc-bugged1-liveness/s4u-mc-bugged1-liveness.tesh
examples/cpp/mc-bugged1/s4u-mc-bugged1.tesh
examples/cpp/mc-bugged2-liveness/s4u-mc-bugged2-liveness.tesh
examples/cpp/mc-bugged2/s4u-mc-bugged2.tesh
examples/cpp/mc-electric-fence/s4u-mc-electric-fence.tesh
examples/cpp/mc-failing-assert/s4u-mc-failing-assert-nodpor.tesh
examples/cpp/mc-failing-assert/s4u-mc-failing-assert-statequality.tesh
examples/cpp/mc-failing-assert/s4u-mc-failing-assert.tesh
examples/cpp/network-ns3/s4u-network-ns3-notime.tesh [moved from examples/cpp/network-ns3/s4u-network-ns3.tesh with 76% similarity]
examples/cpp/network-ns3/s4u-network-ns3-timed.tesh [new file with mode: 0644]
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/platform-failures/s4u-platform-failures.tesh
examples/cpp/synchro-barrier/s4u-mc-synchro-barrier.tesh
examples/cpp/synchro-mutex/s4u-mc-synchro-mutex-stateful.tesh [new file with mode: 0644]
examples/cpp/synchro-mutex/s4u-mc-synchro-mutex.tesh
examples/cpp/synchro-semaphore/s4u-mc-synchro-semaphore.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 [new file with mode: 0644]
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 [new file with mode: 0644]
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/clusters-multicpu/clusters-multicpu.py
examples/python/platform-failures/platform-failures.tesh
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/smpi/CMakeLists.txt
examples/smpi/gemm/gemm.c
examples/smpi/gemm/gemm.tesh
examples/smpi/mc/only_send_deterministic.tesh
examples/smpi/mc/sendsend.tesh
examples/smpi/trace/trace.tesh
examples/smpi/trace_simple/trace_simple.tesh
examples/sthread/CMakeLists.txt
examples/sthread/pthread-mc-mutex-simple.tesh
examples/sthread/pthread-mc-mutex-simpledeadlock.tesh
examples/sthread/pthread-mc-producer-consumer.tesh
examples/sthread/stdobject/stdobject.tesh
include/simgrid/config.h.in
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/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/smpi/mpi.h
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/internal_config.h.in
src/kernel/EngineImpl.cpp
src/kernel/EngineImpl.hpp
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/actor/CommObserver.hpp
src/kernel/actor/Simcall.cpp
src/kernel/context/ContextSwapped.cpp
src/kernel/context/ContextUnix.cpp
src/kernel/lmm/System.cpp
src/kernel/lmm/System.hpp
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/LinkImpl.hpp
src/kernel/resource/Resource.hpp
src/kernel/resource/StandardLinkImpl.cpp
src/kernel/resource/StandardLinkImpl.hpp
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/resource/profile/FutureEvtSet.cpp
src/kernel/routing/NetZoneImpl.cpp
src/kernel/routing/TorusZone.cpp
src/kernel/xml/platf.hpp
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/RemoteApp.cpp
src/mc/api/RemoteApp.hpp
src/mc/api/State.cpp
src/mc/api/State.hpp
src/mc/api/strategy/BasicStrategy.hpp
src/mc/api/strategy/MaxMatchComm.hpp [new file with mode: 0644]
src/mc/api/strategy/MinMatchComm.hpp [new file with mode: 0644]
src/mc/api/strategy/Strategy.hpp
src/mc/api/strategy/UniformStrategy.hpp [new file with mode: 0644]
src/mc/api/strategy/WaitStrategy.hpp [deleted file]
src/mc/explo/CommunicationDeterminismChecker.cpp
src/mc/explo/DFSExplorer.cpp
src/mc/explo/DFSExplorer.hpp
src/mc/explo/Exploration.cpp
src/mc/explo/Exploration.hpp
src/mc/explo/LivenessChecker.cpp
src/mc/explo/LivenessChecker.hpp
src/mc/explo/UdporChecker.cpp
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.cpp
src/mc/explo/udpor/Configuration.hpp
src/mc/explo/udpor/Configuration_test.cpp
src/mc/explo/udpor/EventSet.cpp
src/mc/explo/udpor/EventSet.hpp
src/mc/explo/udpor/EventSet_test.cpp
src/mc/explo/udpor/ExtensionSetCalculator.cpp [new file with mode: 0644]
src/mc/explo/udpor/ExtensionSetCalculator.hpp [new file with mode: 0644]
src/mc/explo/udpor/History.hpp
src/mc/explo/udpor/Unfolding.cpp
src/mc/explo/udpor/Unfolding.hpp
src/mc/explo/udpor/UnfoldingEvent.cpp
src/mc/explo/udpor/UnfoldingEvent.hpp
src/mc/explo/udpor/UnfoldingEvent_test.cpp
src/mc/explo/udpor/Unfolding_test.cpp
src/mc/explo/udpor/maximal_subsets_iterator.cpp
src/mc/explo/udpor/maximal_subsets_iterator.hpp
src/mc/explo/udpor/udpor_forward.hpp
src/mc/explo/udpor/udpor_tests_private.hpp
src/mc/inspect/mc_unw.cpp
src/mc/inspect/mc_unw.hpp
src/mc/mc_base.cpp
src/mc/mc_client_api.cpp
src/mc/mc_config.cpp
src/mc/mc_config.hpp
src/mc/mc_environ.h [new file with mode: 0644]
src/mc/mc_exit.hpp
src/mc/mc_global.cpp
src/mc/mc_record.cpp
src/mc/mc_record.hpp
src/mc/remote/AppSide.cpp
src/mc/remote/AppSide.hpp
src/mc/remote/Channel.cpp
src/mc/remote/Channel.hpp
src/mc/remote/CheckerSide.cpp
src/mc/remote/CheckerSide.hpp
src/mc/remote/RemotePtr.hpp
src/mc/remote/mc_protocol.h
src/mc/sosp/PageStore_test.cpp
src/mc/sosp/Snapshot.cpp
src/mc/sosp/Snapshot.hpp
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/battery.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/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/simgrid/sg_version.cpp
src/smpi/include/smpi_topo.hpp
src/smpi/internals/smpi_config.cpp
src/smpi/internals/smpi_replay.cpp
src/smpi/mpi/smpi_f2c.cpp
src/sthread/ObjectAccess.cpp
src/xbt/mmalloc/mm_legacy.c
src/xbt/mmalloc/mmalloc.h
src/xbt/utils/iter/iterator_wrapping.hpp
src/xbt/utils/iter/powerset.hpp
src/xbt/utils/iter/subsets_tests.cpp
src/xbt/utils/iter/variable_for_loop.hpp
teshsuite/mc/CMakeLists.txt
teshsuite/mc/mutex-handling/mutex-handling.tesh
teshsuite/mc/mutex-handling/without-mutex-handling.tesh
teshsuite/mc/random-bug/random-bug.cpp
teshsuite/mc/random-bug/random-bug.tesh
teshsuite/s4u/CMakeLists.txt
teshsuite/s4u/basic-parsing-test/basic-parsing-test.tesh
teshsuite/s4u/comm-fault-scenarios/comm-fault-scenarios.cpp
teshsuite/s4u/dependencies/dependencies.cpp
teshsuite/s4u/ns3-from-src-to-itself/ns3-from-src-to-itself.tesh
teshsuite/s4u/storage_client_server/storage_client_server.cpp
teshsuite/s4u/vm-suicide/vm-suicide.cpp
teshsuite/smpi/CMakeLists.txt
teshsuite/smpi/MBI/CMakeLists.txt
teshsuite/smpi/coll-allreduce-with-leaks/mc-coll-allreduce-with-leaks.tesh
teshsuite/xbt/signals/signals.cpp
tools/address_sanitizer.supp
tools/cmake/CTestConfig.cmake
tools/cmake/DefinePackages.cmake
tools/cmake/MakeLib.cmake
tools/cmake/Modules/FindGraphviz.cmake
tools/cmake/Modules/FindNS3.cmake
tools/cmake/Modules/FindPAPI.cmake
tools/cmake/Option.cmake
tools/cmake/Tests.cmake
tools/cmake/scripts/my_valgrind.pl
tools/cmake/test_prog/prog_ns3.cpp [new file with mode: 0644]
tools/docker/Dockerfile.build-deps
tools/jenkins/build.sh

index 2839cb3..abd99f5 100644 (file)
@@ -19,5 +19,5 @@ jobs:
           name: Configure, build and test da stuff
           command: |
             mkdir _build && cd _build
-            cmake -Denable_documentation=OFF -Denable_coverage=ON -Denable_model-checking=OFF -Denable_compile_optimizations=OFF -Denable_smpi=ON -Denable_smpi_MPICH3_testsuite=OFF -Denable_compile_warnings=ON ..
+            cmake -Denable_documentation=OFF -Denable_coverage=OFF -Denable_model-checking=OFF -Denable_compile_optimizations=OFF -Denable_smpi=ON -Denable_smpi_MPICH3_testsuite=OFF -Denable_compile_warnings=ON ..
             make -j4 tests && ctest -j4 --output-on-failure
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 67a8202..efb1950 100644 (file)
@@ -86,12 +86,13 @@ pip:
 pages:
   stage: deploy
   script:
-  - pip3 install --requirement docs/requirements.txt
+  - apt install python3-breathe python3-sphinx python3-sphinx-rtd-theme python3-sphinx-copybutton python3-sphinx-tabs
+ # - pip3 install --requirement docs/requirements.txt # Forbidden in Debian:12
   - cd docs
   - LC_ALL=C.UTF-8 ./Build.sh
   - mv build/html ../public
   # - The CSS contains a reference to a font or something, not something we gonna fix on our side
-#not installed   - linkchecker --ignore-url='.*\.css$' ../public
+# not installed   - linkchecker --ignore-url='.*\.css$' ../public
   # From time to time, we should check external links with the
   # following, but it has a lot of false positive
   # - linkchecker --ignore-url='.*\.css$' --check-extern ../public
index 5123dba..4157980 100644 (file)
@@ -77,7 +77,7 @@ if ((NOT DEFINED enable_smpi) OR enable_smpi)
   # configuration where it was saved as smpiff
   unset(CMAKE_Fortran_COMPILER)
 
-  SET(SMPI_FORTRAN 0)
+  SET(SMPI_FORTRAN OFF)
   if(enable_fortran)
     enable_language(Fortran OPTIONAL)
   endif()
@@ -106,7 +106,7 @@ if ((NOT DEFINED enable_smpi) OR enable_smpi)
     ## Request debugging flags for Fortran too
     set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -g")
 
-    set(SMPI_FORTRAN 1)
+    set(SMPI_FORTRAN ON)
   endif(CMAKE_Fortran_COMPILER)
 
 endif()
@@ -195,21 +195,21 @@ include(CheckIncludeFiles)
 include(CheckLibraryExists)
 include(CheckSymbolExists)
 
-set(HAVE_GRAPHVIZ 0)
+set(HAVE_GRAPHVIZ OFF)
 if(minimal-bindings)
   message(STATUS "Don't even look for graphviz, as we build minimal binding libraries.")
 else()
   include(FindGraphviz)
 endif()
 
-set(SIMGRID_HAVE_NS3 0)
+set(SIMGRID_HAVE_NS3 OFF)
 if(enable_ns3)
   include(FindNS3)
   if (SIMGRID_HAVE_NS3)
     if (NOT NS3_VERSION EQUAL "3-dev" AND NS3_VERSION VERSION_LESS "3.28")
       message(FATAL_ERROR "SimGrid needs ns-3 in version 3.28 or higher. Please upgrade or disable that cmake option.")
     endif()
-    set(SIMGRID_HAVE_NS3 1)
+    set(SIMGRID_HAVE_NS3 ON)
     set(SIMGRID_DEP "${SIMGRID_DEP} ${NS3_LIBRARIES}")
   else()
     message(FATAL_ERROR "Cannot find ns-3. Please install it (apt-get install ns3 libns3-dev) or disable that cmake option")
@@ -217,11 +217,11 @@ if(enable_ns3)
 endif()
 
 ### Check for Eigen library
-set(SIMGRID_HAVE_EIGEN3 0)
+set(SIMGRID_HAVE_EIGEN3 OFF)
 find_package (Eigen3 3.3 CONFIG
               HINTS ${EIGEN3_HINT})
 if (Eigen3_FOUND)
-  set(SIMGRID_HAVE_EIGEN3 1)
+  set(SIMGRID_HAVE_EIGEN3 ON) 
   message(STATUS "Found Eigen3: ${EIGEN3_INCLUDE_DIR}")
   include_directories(${EIGEN3_INCLUDE_DIR})
   if ("3.3.4" VERSION_EQUAL EIGEN3_VERSION_STRING AND CMAKE_COMPILER_IS_GNUCC)
@@ -231,6 +231,7 @@ if (Eigen3_FOUND)
 else()
   message(STATUS "Disabling model BMF because Eigen3 was not found. If it's installed, use EIGEN3_HINT to hint cmake about the location of Eigen3Config.cmake")
 endif()
+mark_as_advanced(Eigen3_DIR)
 
 # Check for our JSON dependency
 set(SIMGRID_HAVE_JSON 0)
@@ -246,14 +247,16 @@ if (nlohmann_json_FOUND)
   endif()
   message(STATUS "Found nlohmann_json: ${NLOHMANN_JSON_INCLUDE_DIR}")
 endif()
+mark_as_advanced(nlohmann_json_DIR)
 
-set(HAVE_PAPI 0)
+set(HAVE_PAPI OFF)
 if(enable_smpi_papi)
   include(FindPAPI)
   if (NOT HAVE_PAPI)
     message(FATAL_ERROR "Cannot find PAPI. Please install it (apt-get install papi-tools libpapi-dev) or disable PAPI bindings.")
   endif()
 endif()
+mark_as_advanced(PAPI_PREFIX)
 
 # But we do need the core of Boost
 # cmake before 3.13.1 does not know about stacktrace components. Fix it.
@@ -291,10 +294,10 @@ set(_Boost_STACKTRACE_ADDR2LINE_HEADERS "boost/stacktrace.hpp")
 
       if(Boost_CONTEXT_FOUND)
         message (STATUS "  context: found. Activating Boost contexts.")
-        set(HAVE_BOOST_CONTEXTS 1)
+        set(HAVE_BOOST_CONTEXTS ON)
       else()
         message (STATUS "  context: MISSING. Install libboost-context-dev for this optional feature.")
-        set(HAVE_BOOST_CONTEXTS 0)
+        set(HAVE_BOOST_CONTEXTS OFF)
       endif()
     endif()
   else()
@@ -310,6 +313,10 @@ set(_Boost_STACKTRACE_ADDR2LINE_HEADERS "boost/stacktrace.hpp")
       endif()
     endif()
   endif()
+mark_as_advanced(Boost_CONTEXT_LIBRARY_RELEASE)
+mark_as_advanced(Boost_INCLUDE_DIR)
+mark_as_advanced(Boost_STACKTRACE_ADDR2LINE_LIB)
+mark_as_advanced(Boost_STACKTRACE_BACKTRACE_LIB)
 
 # Checks for header libraries functions.
 CHECK_LIBRARY_EXISTS(rt      clock_gettime           "" HAVE_POSIX_GETTIME)
@@ -355,53 +362,57 @@ if(enable_model-checking AND minimal-bindings)
   message(FATAL_ERROR "Compile-time option 'minimal-bindings' cannot be enabled with 'model-checking'")
 endif()
 
-if(HAVE_MMAP)
-  SET(HAVE_MMALLOC 1)
-else()
-  SET(HAVE_MMALLOC 0)
-  if(enable_model-checking)
-    message(STATUS "Warning: support for model-checking has been disabled because you are missing either mmap or __thread.")
-  endif()
-  SET(enable_model-checking 0)
-endif()
-
 if(enable_mallocators)
   SET(SIMGRID_HAVE_MALLOCATOR 1)
 else()
   SET(SIMGRID_HAVE_MALLOCATOR 0)
 endif()
 
+SET(SIMGRID_HAVE_MC OFF)
+SET(SIMGRID_HAVE_STATEFUL_MC OFF)
+SET(HAVE_MMALLOC 0)
+
+find_package(Libevent)
+if(Libevent_FOUND)
+  message(STATUS "Found libevent. The stateless model-checking can be enabled.")
+  include_directories(${LIBEVENT_INCLUDE_DIR})
+  set(SIMGRID_DEP "${SIMGRID_DEP} ${LIBEVENT_LIBRARIES}")
+  SET(SIMGRID_HAVE_MC ON)
+else()
+  message(STATUS "libevent not found. Please install libevent-dev to enable the SimGrid model checker.")
+endif()
+mark_as_advanced(LIBEVENT_LIBRARY)
+mark_as_advanced(LIBEVENT_THREADS_LIBRARY)
+
 if(enable_model-checking)
   include(FindLibunwind)
-  if(HAVE_LIBUNWIND)
-    SET(SIMGRID_DEP "${SIMGRID_DEP} ${LIBUNWIND_LIBRARIES}")
+  find_package(Libdw)
+  find_package(Libelf)
+  if(HAVE_MMAP AND HAVE_LIBUNWIND AND Libdw_FOUND AND Libelf_FOUND AND Libevent_FOUND)
+    message(STATUS "All dependencies found. The stateful model-checking can be enabled.")
+    SET(SIMGRID_HAVE_STATEFUL_MC ON)
+    SET(HAVE_MMALLOC 1)
+    SET(SIMGRID_DEP "${SIMGRID_DEP} ${LIBUNWIND_LIBRARIES} ${LIBELF_LIBRARIES} ${LIBDW_LIBRARIES}")
+    include_directories(${LIBDW_INCLUDE_DIR} ${LIBELF_INCLUDE_DIR})
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gdwarf-4")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gdwarf-4")
   else()
-    message(FATAL_ERROR "Please install libunwind-dev libdw-dev libelf-dev libevent-dev if you want to compile the SimGrid model checker.")
+    message(STATUS "Please install libunwind-dev libdw-dev libelf-dev libevent-dev to enable the stateful model checker.")
+    set(HAVE_MMALLOC 0)
   endif()
-  find_package(Libdw REQUIRED)
-  find_package(Libelf REQUIRED)
-  find_package(Libevent REQUIRED)
-  include_directories(${LIBDW_INCLUDE_DIR} ${LIBELF_INCLUDE_DIR} ${LIBEVENT_INCLUDE_DIR})
-  set(SIMGRID_DEP "${SIMGRID_DEP} ${LIBEVENT_LIBRARIES} ${LIBELF_LIBRARIES} ${LIBDW_LIBRARIES}")
-  set(SIMGRID_HAVE_MC 1)
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gdwarf-4")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gdwarf-4")
-else()
-  SET(SIMGRID_HAVE_MC 0)
-  set(HAVE_MMALLOC 0)
 endif()
 mark_as_advanced(PATH_LIBDW_H)
 mark_as_advanced(PATH_LIBDW_LIB)
 
-if (enable_model-checking AND enable_ns3)
+if (SIMGRID_HAVE_STATEFUL_MC AND enable_ns3) 
   message(WARNING "Activating both model-checking and ns-3 bindings is considered experimental.")
 endif()
 
 if(enable_smpi)
-  SET(HAVE_SMPI 1)
-  SET(HAVE_PRIVATIZATION 1)
+  SET(HAVE_SMPI ON)
+  SET(HAVE_PRIVATIZATION ON)
 else()
-  SET(HAVE_SMPI 0)
+  SET(HAVE_SMPI OFF)
 endif()
 
 #--------------------------------------------------------------------------------------------------
@@ -464,7 +475,7 @@ else()
   endif()
 endif()
 # If the test ran well, remove the test binary
-file(REMOVE test_stackgrowth)
+file(REMOVE ${CMAKE_BINARY_DIR}/test_stackgrowth)
 #--------------------------------------------------------------------------------------------------
 
 ###############
@@ -744,6 +755,8 @@ SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES
 add_custom_target(tests    COMMENT "Recompiling the tests")
 add_custom_target(tests-mc COMMENT "Recompiling the MC tests and tools.")
 add_dependencies(tests tests-mc)
+add_custom_target(tests-ns3 COMMENT "Recompiling the ns3 tests and tools.")
+add_dependencies(tests tests-ns3)
 
 ### Build some Maintainer files
 include(${CMAKE_HOME_DIRECTORY}/tools/cmake/MaintainerMode.cmake)
@@ -933,9 +946,12 @@ message("          Privatization .............: ${HAVE_PRIVATIZATION}")
 message("          PAPI support ..............: ${HAVE_PAPI}")
 message("        Compile Boost.Context support: ${HAVE_BOOST_CONTEXTS}")
 message("")
+message("        Model checking ..............: ${SIMGRID_HAVE_MC}")
+message("          Stateful model checking ...: ${SIMGRID_HAVE_STATEFUL_MC}")
+message("          MBI testsuite .............: ${enable_smpi_MBI_testsuite}")
+message("")
 message("        Maintainer mode .............: ${enable_maintainer_mode}")
 message("        Documentation ...............: ${enable_documentation}")
-message("        Model checking ..............: ${SIMGRID_HAVE_MC}")
 message("        Graphviz mode ...............: ${HAVE_GRAPHVIZ}")
 message("        Mallocators .................: ${enable_mallocators}")
 message("")
index 41bc62f..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:
@@ -18,6 +20,26 @@ S4U:
  - Full simDAG integration: Activity::start() actually starts only when all dependencies
    are fullfiled. If it cannot be started right away, it will start as soon as it becomes
    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.
 
 Kernel:
  - optimize an internal datastructure (use a set instead of a list for ongoing activities),
@@ -56,6 +78,10 @@ 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
+   configuration option.
+ - Stateless model-checking is now usable on any system, including Mac OSX and ARM processors.
 
 XBT:
  - simgrid::xbt::cmdline and simgrid::xbt::binary_name are gone.
@@ -654,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 ce4e415..5ad678c 100644 (file)
@@ -345,10 +345,21 @@ include examples/cpp/network-ns3/crosstraffic_d.xml
 include examples/cpp/network-ns3/dogbone_d.xml
 include examples/cpp/network-ns3/one_cluster_d.xml
 include examples/cpp/network-ns3/onelink_d.xml
+include examples/cpp/network-ns3/s4u-network-ns3-notime.tesh
+include examples/cpp/network-ns3/s4u-network-ns3-timed.tesh
 include examples/cpp/network-ns3/s4u-network-ns3.cpp
-include examples/cpp/network-ns3/s4u-network-ns3.tesh
 include examples/cpp/network-wifi/s4u-network-wifi.cpp
 include examples/cpp/network-wifi/s4u-network-wifi.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
@@ -384,6 +395,7 @@ include examples/cpp/synchro-condition-variable-waituntil/s4u-synchro-condition-
 include examples/cpp/synchro-condition-variable-waituntil/s4u-synchro-condition-variable-waituntil.tesh
 include examples/cpp/synchro-condition-variable/s4u-synchro-condition-variable.cpp
 include examples/cpp/synchro-condition-variable/s4u-synchro-condition-variable.tesh
+include examples/cpp/synchro-mutex/s4u-mc-synchro-mutex-stateful.tesh
 include examples/cpp/synchro-mutex/s4u-mc-synchro-mutex.tesh
 include examples/cpp/synchro-mutex/s4u-synchro-mutex.cpp
 include examples/cpp/synchro-mutex/s4u-synchro-mutex.tesh
@@ -464,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
@@ -1824,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
@@ -1923,6 +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/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
@@ -2162,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
@@ -2180,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
@@ -2188,6 +2226,8 @@ include src/mc/explo/udpor/Configuration_test.cpp
 include src/mc/explo/udpor/EventSet.cpp
 include src/mc/explo/udpor/EventSet.hpp
 include src/mc/explo/udpor/EventSet_test.cpp
+include src/mc/explo/udpor/ExtensionSetCalculator.cpp
+include src/mc/explo/udpor/ExtensionSetCalculator.hpp
 include src/mc/explo/udpor/History.cpp
 include src/mc/explo/udpor/History.hpp
 include src/mc/explo/udpor/History_test.cpp
@@ -2225,6 +2265,7 @@ include src/mc/mc_base.hpp
 include src/mc/mc_client_api.cpp
 include src/mc/mc_config.cpp
 include src/mc/mc_config.hpp
+include src/mc/mc_environ.h
 include src/mc/mc_exit.hpp
 include src/mc/mc_forward.hpp
 include src/mc/mc_global.cpp
@@ -2277,6 +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/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
@@ -2627,6 +2670,7 @@ include tools/cmake/scripts/my_valgrind.pl
 include tools/cmake/scripts/update_tesh.pl
 include tools/cmake/test_prog/prog_asan.cpp
 include tools/cmake/test_prog/prog_makecontext.c
+include tools/cmake/test_prog/prog_ns3.cpp
 include tools/cmake/test_prog/prog_stackgrowth.c
 include tools/cmake/test_prog/prog_stacksetup.c
 include tools/cmake/test_prog/prog_tsan.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 a98401b..d6238d6 100644 (file)
@@ -1,5 +1,5 @@
 breathe>=4.26
-sphinx>=3.4.3,<4.0
+sphinx
 sphinx_rtd_theme>=0.5.2
 # sphinx_tabs v1.2.1 is required for Sphinx 2
 sphinx_tabs>=1.2.1
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 4751ceb..c58f470 100644 (file)
@@ -100,8 +100,10 @@ boost recommended components (optional).
   - On Debian / Ubuntu: ``apt install libboost-context-dev libboost-stacktrace-dev``
 python bindings (optional):
   - On Debian / Ubuntu: ``apt install pybind11-dev python3-dev``
-Model-checking dependencies (optional)
-  - On Debian / Ubuntu: ``apt install libunwind-dev libdw-dev libelf-dev libevent-dev``
+Model-checking mandatory dependencies
+  - On Debian / Ubuntu: ``apt install libevent-dev``
+Model-checking optional dependencies
+  - On Debian / Ubuntu: ``apt install libunwind-dev libdw-dev libelf-dev``
 Eigen3 (optional)
   - On Debian / Ubuntu: ``apt install libeigen3-dev``
   - On CentOS / Fedora: ``dnf install eigen3-devel``
@@ -242,7 +244,7 @@ enable_mallocators (ON/off)
   code, but it may fool the debuggers.
 
 enable_model-checking (on/OFF)
-  Activates the formal verification mode. This will hinder simulation speed even when the model checker is not activated at run
+  Activates the liveness verification mode. This will hinder simulation speed even when the model checker is not activated at run
   time, because some optimizations such as LTO must be disabled at compile time. You need to have the :ref:`required
   build-dependencies <install_src_deps>` to activate this option.
 
@@ -399,7 +401,7 @@ Windows-specific instructions
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 The best solution to get SimGrid working on windows is to install the
-Ubuntu subsystem of Windows 10. All of SimGrid (but the model checker)
+Ubuntu subsystem of Windows 10. All of SimGrid (but the liveness model checker)
 works in this setting. Native builds never really worked, and they are
 disabled starting with SimGrid v3.33.
 
index 9b54d2d..1688d80 100644 (file)
@@ -332,21 +332,26 @@ distributed algorithms such as leader election or causal broadcast.
 ns-3 as a SimGrid model
 ***********************
 
-The **ns-3 based model** is the most accurate network model in SimGrid. It relies on the well-known
-`ns-3 packet-level network simulator <http://www.nsnam.org>`_ to compute full timing information related to network
-transfers. This
-model is much slower than the LMM-based models. This is because ns-3 simulates the movement of every network packet involved in
-every communication, while the LMM-based models only recompute the respective instantaneous speeds of the currently ongoing
-communications when a communication starts or stops.
+The **ns-3 based model** is the most accurate network model in SimGrid. It relies on the well-known `ns-3 packet-level network
+simulator <http://www.nsnam.org>`_ to compute full timing information related to network transfers. This model is much slower
+than the LMM-based models. This is because ns-3 simulates the movement of every network packet involved in every communication,
+while the LMM-based models only recompute the respective instantaneous speeds of the currently ongoing communications when a
+communication starts or stops. In other terms, both SimGrid and ns-3 are fast and highly optimized, but while SimGrid only
+depends on application-level events (starting and stoping of communications), ns-3 depends on network-level events (sending a
+packet).
 
 You need to install ns-3 and recompile SimGrid accordingly to use this model.
 
-The SimGrid/ns-3 binding only contains features that are common to both systems. Not all ns-3 models are available from
-SimGrid (only the TCP and WiFi ones are), while not all SimGrid platform files can be used in conjunction with ns-3
-(routes must be of length 1). Note also that the platform built in ns-3 from the SimGrid
-description is very basic. Finally, communicating from a host to
-itself is forbidden in ns-3, so every such communication is simulated to take zero time.
+The SimGrid/ns-3 binding only contains features that are common to both systems. Not all ns-3 models are available from SimGrid
+(only the TCP and WiFi ones are), while not all SimGrid platform files can be used in conjunction with ns-3 (routes must be of
+length 1). Note also that the platform built in ns-3 from the SimGrid description is very basic. Finally, communicating from a
+host to itself is forbidden in ns-3, so every such communication is simulated to take zero time.
 
+By default, the ns-3 model in SimGrid is not idempotent, unless you patch your version of ns-3 with [this
+patch](https://gitlab.com/nsnam/ns-3-dev/-/merge_requests/1338). It is perfectly OK to have a non-idempotent model in SimGrid as
+long as you only have only one such model, and as long as you don't use utterly advanced things in SimGrid. If you do want to
+have an idempotent ns-3, apply the previously mentioned patch, and recompile SimGrid. It should detect the patch and react
+accordingly.
 
 Compiling the ns-3/SimGrid binding
 ==================================
index 4511223..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,6 +219,18 @@ Battery
 
 .. doxygengroup:: plugin_battery
 
+.. _plugin_task:
+
+Task
+===========
+
+.. doxygengroup:: plugin_task
+
+.. _plugin_photovoltaic:
+
+Photovoltaic
+===========
 
+.. 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 621eaf2..37217f2 100644 (file)
@@ -148,6 +148,7 @@ The execution of this code should give you the following output:
 .. literalinclude:: ../../examples/cpp/dag-tuto/s4u-dag-tuto.tesh
    :language: none
    :lines: 4-
+
 Lab 2: Import a DAG from a file
 -------------------------------
 
@@ -214,3 +215,196 @@ It can be imported as a vector of Activities into Simgrid using :cpp:func:`simgr
 
 .. literalinclude:: ../../examples/cpp/dag-from-dax-simple/s4u-dag-from-dax-simple.cpp
    :language: cpp
+
+Lab 3: Scheduling with the Min-Min algorithm
+--------------------------------------------
+
+In this lab we present how to schedule activities imported from a DAX file using the 
+`Min-Min algorithm <https://www.researchgate.net/figure/The-Traditional-Min-Min-Scheduling-Algorithm_fig5_236346423>`_.
+
+The source code for this lab can be found `here <https://framagit.org/simgrid/simgrid/-/blob/stable/examples/cpp/dag-scheduling/s4u-dag-scheduling.cpp>`_.
+
+For code readability we first create the `sg4` namespace.
+
+.. code-block:: cpp
+
+   namespace sg4 = simgrid::s4u;
+
+The core mechanism of the algorithm lies in three functions. 
+They respectively serve the purpose of finding tasks to schedule, 
+finding the best host to execute them and properly scheduling them.
+
+Find Tasks to Schedule
+......................
+
+The role of this function is to retrieve tasks that are ready to be scheduled, i.e, that have their dependencies solved.
+
+.. literalinclude:: ../../examples/cpp/dag-scheduling/s4u-dag-scheduling.cpp
+   :language: cpp
+   :lines: 15-38
+
+Find the Best Placement
+.......................
+
+Once we have a task ready to be scheduled, we need to find the best placement for it.
+This is done by evaluating the earliest finish time among all hosts.
+It depends on the duration of the data transfers of the parents of this task to this host.
+
+.. literalinclude:: ../../examples/cpp/dag-scheduling/s4u-dag-scheduling.cpp
+   :language: cpp
+   :lines: 40-91
+
+Schedule a Task
+...............
+
+When the best host has been found, the task is scheduled on it:
+
+* it sets the host of the task to schedule
+* it stores the finish time of this task on the host
+* it sets the destination of parents communication
+* it sets the source of any child communication.
+
+.. literalinclude:: ../../examples/cpp/dag-scheduling/s4u-dag-scheduling.cpp
+   :language: cpp
+   :lines: 93-113
+
+Mixing it all Together
+......................
+
+Now that we have the key components of the algorithm let's merge them inside the main function.
+
+.. code-block:: cpp
+
+   int main(int argc, char** argv)
+   {
+   ...
+
+First, we initialize the Simgrid Engine.
+
+.. code-block:: cpp
+
+   sg4::Engine e(&argc, argv);
+
+The Min-Min algorithm schedules unscheduled tasks. 
+To keep track of them we make use of the method :cpp:func:`simgrid::s4u::Engine::track_vetoed_activities`.
+
+.. code-block:: cpp
+
+   std::set<sg4::Activity*> vetoed;
+   e.track_vetoed_activities(&vetoed);
+
+We add the following callback that will be triggered at the end of execution activities.
+This callback stores the finish time of the execution, 
+to use it as a start time for any subsequent communications.
+
+.. code-block:: cpp
+
+  sg4::Activity::on_completion_cb([](sg4::Activity const& activity) {
+    // 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()) {
+      auto* comm = dynamic_cast<sg4::Comm*>(succ.get());
+      if (comm != nullptr) {
+        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);
+      }
+    }
+  });
+
+We load the platform and force sequential execution on hosts.
+
+.. code-block:: cpp
+
+   e.load_platform(argv[1]);
+
+  /* Mark all hosts as sequential, as it ought to be in such a scheduling example.
+   *
+   * It means that the hosts can only compute one thing at a given time. If an execution already takes place on a given
+   * host, any subsequently started execution will be queued until after the first execution terminates */
+  for (auto const& host : e.get_all_hosts()) {
+    host->set_concurrency_limit(1);
+    host->set_data(new double(0.0));
+  }
+
+The tasks are imported from a DAX file.
+
+.. code-block:: cpp
+
+  /* load the DAX file */
+  auto dax = sg4::create_DAG_from_DAX(argv[2]);
+
+We look for the best host for the root task and schedule it. 
+We then advance the simulation to unlock next schedulable tasks.
+
+.. code-block:: cpp
+
+  /* Schedule the root first */
+  double finish_time;
+  auto* root = static_cast<sg4::Exec*>(dax.front().get());
+  auto host  = get_best_host(root, &finish_time);
+  schedule_on(root, host);
+  e.run();
+
+Then, we get to the major loop of the algorithm.
+This loop goes on until all tasks have been scheduled and executed.
+It starts by finding ready tasks using `get_ready_tasks`. 
+It iteratively looks for the task that will finish first among ready tasks using `get_best_host`, and place it using `schedule_on`. 
+When no more tasks can be placed, we advance the simulation.
+
+.. code-block:: cpp
+
+  while (not vetoed.empty()) {
+    XBT_DEBUG("Start new scheduling round");
+    /* Get the set of ready tasks */
+    auto ready_tasks = get_ready_tasks(dax);
+    vetoed.clear();
+
+    if (ready_tasks.empty()) {
+      /* there is no ready exec, let advance the simulation */
+      e.run();
+      continue;
+    }
+    /* For each ready exec:
+     * get the host that minimizes the completion time.
+     * select the exec that has the minimum completion time on its best host.
+     */
+    double min_finish_time   = std::numeric_limits<double>::max();
+    sg4::Exec* selected_task = nullptr;
+    sg4::Host* selected_host = nullptr;
+
+    for (auto exec : ready_tasks) {
+      XBT_DEBUG("%s is ready", exec->get_cname());
+      double finish_time;
+      host = get_best_host(exec, &finish_time);
+      if (finish_time < min_finish_time) {
+        min_finish_time = finish_time;
+        selected_task   = exec;
+        selected_host   = host;
+      }
+    }
+
+    XBT_INFO("Schedule %s on %s", selected_task->get_cname(), selected_host->get_cname());
+    schedule_on(selected_task, selected_host, min_finish_time);
+
+    ready_tasks.clear();
+    e.run();
+  }
+
+Finally, we clean up the memory.
+
+.. code-block:: cpp
+
+   /* Cleanup memory */
+   for (auto const& h : e.get_all_hosts())
+     delete h->get_data<double>();
+
+
+
+
+
+
+
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 74e5c71..5aadcd8 100644 (file)
@@ -3,7 +3,7 @@
 p Testing a simple master/worker example application handling failures TCP crosstraffic DISABLED
 
 ! output sort 19
-$ ${bindir:=.}/c-platform-failures --log=xbt_cfg.thres:critical --log=no_loc ${platfdir}/small_platform_failures.xml ${srcdir:=.}/../../cpp/platform-failures/s4u-platform-failures_d.xml --cfg=path:${srcdir} --cfg=network/crosstraffic:0 "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=res_cpu.t:verbose
+$ ${bindir:=.}/c-platform-failures --log=xbt_cfg.thres:critical --log=no_loc ${platfdir}/small_platform_failures.xml ${srcdir:=.}/../../cpp/platform-failures/s4u-platform-failures_d.xml --cfg=network/crosstraffic:0 "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=res_cpu.t:verbose
 > [  0.000000] (0:maestro@) Cannot launch actor 'worker' on failed host 'Fafard'
 > [  0.000000] (0:maestro@) Starting actor worker(Fafard) failed because its host is turned off.
 > [  0.000000] (1:master@Tremblay) Got 5 workers and 20 tasks to process
@@ -108,7 +108,7 @@ $ ${bindir:=.}/c-platform-failures --log=xbt_cfg.thres:critical --log=no_loc ${p
 p Testing a simple master/worker example application handling failures. TCP crosstraffic ENABLED
 
 ! output sort 19
-$ ${bindir:=.}/c-platform-failures --log=xbt_cfg.thres:critical --log=no_loc ${platfdir}/small_platform_failures.xml ${srcdir:=.}/../../cpp/platform-failures/s4u-platform-failures_d.xml --cfg=path:${srcdir} "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=res_cpu.t:verbose
+$ ${bindir:=.}/c-platform-failures --log=xbt_cfg.thres:critical --log=no_loc ${platfdir}/small_platform_failures.xml ${srcdir:=.}/../../cpp/platform-failures/s4u-platform-failures_d.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=res_cpu.t:verbose
 > [  0.000000] (0:maestro@) Cannot launch actor 'worker' on failed host 'Fafard'
 > [  0.000000] (0:maestro@) Starting actor worker(Fafard) failed because its host is turned off.
 > [  0.000000] (1:master@Tremblay) Got 5 workers and 20 tasks to process
@@ -217,4 +217,4 @@ p   complex with such an integration test. One day, we will setup a set of
 p   unit tests for the model's solver, and such issues will be addressable again.
 p For the time being, I just give up, sorry.
 
-p $ ${bindir:=.}/c-platform-failures --log=xbt_cfg.thres:critical --log=no_loc ${platfdir}/small_platform_failures.xml ${srcdir:=.}/../../cpp/platform-failures/s4u-platform-failures_d.xml --cfg=path:${srcdir} --cfg=cpu/optim:TI "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=res_cpu.t:verbose
+p $ ${bindir:=.}/c-platform-failures --log=xbt_cfg.thres:critical --log=no_loc ${platfdir}/small_platform_failures.xml ${srcdir:=.}/../../cpp/platform-failures/s4u-platform-failures_d.xml --cfg=cpu/optim:TI "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=res_cpu.t:verbose
index 10fd24a..709bebe 100644 (file)
@@ -17,12 +17,25 @@ set(_actor-stacksize_factories "^thread") # Threads ignore modifications of the
 set(_maestro-set_factories "thread")
 
 if(SIMGRID_HAVE_MC)
-  # These tests timeout with threads, maybe because of dwarf parsing? not sure
+  # These tests timeout with threads, not sure why
   foreach(example mc-bugged1 mc-bugged2 mc-failing-assert mc-electric-fence)
-     set(_${example}_factories "^thread") # Timeout
-     add_dependencies(tests-mc s4u-${example})
+    set(_${example}_factories "^thread") # Timeout
+    add_dependencies(tests-mc s4u-${example})
+  endforeach()
+
+  if(enable_coverage)
+    foreach (example mc-bugged1 mc-bugged2 mc-electric-fence mc-failing-assert)
+      ADD_TEST(cover-${example} ${CMAKE_CURRENT_BINARY_DIR}/${example}/s4u-${example} ${CMAKE_HOME_DIRECTORY}/examples/platforms/model_checker_platform.xml)
+    endforeach()
+  endif()
+else()
+  foreach (example mc-bugged1 mc-bugged2  mc-centralized-mutex mc-failing-assert mc-electric-fence)
+    set(_${example}_disable 1)
   endforeach()
+endif()
 
+set(tesh_files ${tesh_files} ${CMAKE_CURRENT_SOURCE_DIR}/synchro-mutex/s4u-mc-synchro-mutex-stateful.tesh)
+if(SIMGRID_HAVE_STATEFUL_MC)
   if(HAVE_C_STACK_CLEANER)
     add_executable       (s4u-mc-bugged1-liveness-cleaner-on  EXCLUDE_FROM_ALL s4u-mc-bugged1-liveness/s4u-mc-bugged1-liveness.cpp)
     target_link_libraries(s4u-mc-bugged1-liveness-cleaner-on  simgrid)
@@ -35,6 +48,14 @@ if(SIMGRID_HAVE_MC)
     add_dependencies(tests-mc s4u-mc-bugged1-liveness-cleaner-off)
   endif()
 
+  ADD_TESH(s4u-mc-synchro-mutex-stateful
+     --setenv bindir=${CMAKE_CURRENT_BINARY_DIR}/synchro-mutex
+     --setenv libdir=${CMAKE_BINARY_DIR}/lib
+     --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms
+     --setenv srcdir=${CMAKE_CURRENT_SOURCE_DIR}/synchro-mutex
+     --cd ${CMAKE_CURRENT_SOURCE_DIR}/synchro-mutex
+      ${CMAKE_HOME_DIRECTORY}/examples/cpp/synchro-mutex/s4u-mc-synchro-mutex-stateful.tesh)
+
   # Model-checking liveness
   if(HAVE_UCONTEXT_CONTEXTS AND SIMGRID_PROCESSOR_x86_64)
     # liveness model-checking works only on 64bits (for now ...)
@@ -62,15 +83,11 @@ if(SIMGRID_HAVE_MC)
   endif()
 
   if(enable_coverage)
-    foreach (example mc-bugged1 mc-bugged2 mc-electric-fence mc-failing-assert)
-      ADD_TEST(cover-${example} ${CMAKE_CURRENT_BINARY_DIR}/${example}/s4u-${example} ${CMAKE_HOME_DIRECTORY}/examples/platforms/model_checker_platform.xml)
-    endforeach()
     ADD_TEST(cover-mc-bugged1-liveness ${CMAKE_CURRENT_BINARY_DIR}/mc-bugged1-liveness/s4u-mc-bugged1-liveness ${CMAKE_HOME_DIRECTORY}/examples/platforms/small_platform.xml 1 1001)
   endif()
 
 else()
-  foreach (example mc-bugged1 mc-bugged2  mc-centralized-mutex mc-failing-assert mc-electric-fence
-                   mc-bugged1-liveness mc-bugged2-liveness)
+  foreach (example mc-bugged1-liveness mc-bugged2-liveness)
     set(_${example}_disable 1)
   endforeach()
 endif()
@@ -81,12 +98,12 @@ foreach (example synchro-barrier synchro-mutex synchro-semaphore)
 
   if (SIMGRID_HAVE_MC)
     ADD_TESH(s4u-mc-${example}
-             --setenv bindir=${CMAKE_CURRENT_BINARY_DIR}/${example}
-             --setenv libdir=${CMAKE_BINARY_DIR}/lib
-             --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms
-             --setenv srcdir=${CMAKE_CURRENT_SOURCE_DIR}/${example}
-             --cd ${CMAKE_CURRENT_SOURCE_DIR}/${example}
-             ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-mc-${example}.tesh)
+              --setenv bindir=${CMAKE_CURRENT_BINARY_DIR}/${example}
+              --setenv libdir=${CMAKE_BINARY_DIR}/lib
+              --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms
+              --setenv srcdir=${CMAKE_CURRENT_SOURCE_DIR}/${example}
+              --cd ${CMAKE_CURRENT_SOURCE_DIR}/${example}
+              ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-mc-${example}.tesh)
 
     add_dependencies(tests-mc s4u-${example})
   endif()
@@ -108,7 +125,24 @@ if (NOT SIMGRID_HAVE_JSON)
   set(_dag-from-json-simple_disable 1)
 endif()
 
-if(NOT SIMGRID_HAVE_NS3)
+if(SIMGRID_HAVE_NS3)
+  if(NS3_VERSION VERSION_GREATER_EQUAL 3.37)
+    set(_network-ns3_teshfile         ${CMAKE_HOME_DIRECTORY}/examples/cpp/network-ns3/s4u-network-ns3-timed.tesh)
+    set(tesh_files    ${tesh_files}   ${CMAKE_HOME_DIRECTORY}/examples/cpp/network-ns3/s4u-network-ns3-notime.tesh)
+  else()
+    set(_network-ns3_teshfile         ${CMAKE_HOME_DIRECTORY}/examples/cpp/network-ns3/s4u-network-ns3-notime.tesh)
+    set(tesh_files    ${tesh_files}   ${CMAKE_HOME_DIRECTORY}/examples/cpp/network-ns3/s4u-network-ns3-timed.tesh)
+  endif()
+
+foreach (example network-ns3 network-ns3-wifi)
+  add_dependencies(tests-ns3 s4u-${example})
+endforeach()
+
+else()
+  # Even if ns3 is not found, we need to override the teshfile name and make sure that everything gets included in the archive
+  set(_network-ns3_teshfile         ${CMAKE_HOME_DIRECTORY}/examples/cpp/network-ns3/s4u-network-ns3-notime.tesh)
+  set(tesh_files    ${tesh_files}   ${CMAKE_HOME_DIRECTORY}/examples/cpp/network-ns3/s4u-network-ns3-timed.tesh)
+
   foreach (example network-ns3 network-ns3-wifi)
     set(_${example}_disable 1)
   endforeach()
@@ -134,6 +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
+                 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
@@ -146,6 +182,11 @@ foreach (example activity-testany activity-waitany
     set(_${example}_sources ${example}/s4u-${example}.cpp)
   endif()
 
+  # Use default tesh file unless specified otherwise
+  if(NOT DEFINED _${example}_teshfile)
+    set(_${example}_teshfile ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}.tesh)
+  endif()
+
   if(NOT DEFINED _${example}_disable)
     add_executable       (s4u-${example} EXCLUDE_FROM_ALL ${_${example}_sources})
     add_dependencies     (tests s4u-${example})
@@ -165,19 +206,20 @@ foreach (example activity-testany activity-waitany
                                       --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms
                                       --setenv srcdir=${CMAKE_CURRENT_SOURCE_DIR}/${example}
                                       --cd ${CMAKE_CURRENT_SOURCE_DIR}/${example}
-                                      ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}.tesh)
+                                      ${_${example}_teshfile})
   else()
     message(STATUS "Example ${example} disabled, thus not compiled.")
     unset(_${example}_disable)
   endif()
 
-  set(tesh_files    ${tesh_files}    ${CMAKE_CURRENT_SOURCE_DIR}/${example}/s4u-${example}.tesh)
+  set(tesh_files    ${tesh_files}    ${_${example}_teshfile})
   foreach(file ${_${example}_sources})
     set(examples_src  ${examples_src} ${CMAKE_CURRENT_SOURCE_DIR}/${file})
   endforeach()
 
   unset(_${example}_factories)
   unset(_${example}_sources)
+  unset(_${example}_teshfile)
 endforeach()
 
 
@@ -217,18 +259,20 @@ endforeach()
 
 # Test non-DPOR reductions on a given MC test
 foreach(example mc-failing-assert)
-  if(SIMGRID_HAVE_MC)
 # State equality is not tested because it would take about 15 hours to run that test on my machine.
 # We should first optimize mmalloc_heap_differ() which takes ~4sec for each pair to compare (maybe {175 x 174/ 2} pairs here)
 # See the comment on mmalloc_heap_differ() in compare.cpp for more info on why it's hard to optimize.
 #
+#  if(SIMGRID_HAVE_STATEFUL_MC)
 #    ADD_TESH(s4u-${example}-statequality  --setenv bindir=${CMAKE_CURRENT_BINARY_DIR}/${example}
 #                                      --setenv libdir=${CMAKE_BINARY_DIR}/lib
 #                                      --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms
 #                                      --setenv srcdir=${CMAKE_CURRENT_SOURCE_DIR}/${example}
 #                                      --cd ${CMAKE_CURRENT_SOURCE_DIR}/${example}
 #                                      ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}-statequality.tesh)
+#  endif()
 
+  if(SIMGRID_HAVE_MC)
     ADD_TESH(s4u-${example}-nodpor    --setenv bindir=${CMAKE_CURRENT_BINARY_DIR}/${example}
                                       --setenv libdir=${CMAKE_BINARY_DIR}/lib
                                       --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms
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 d20de08..7b76aa9 100644 (file)
 XBT_LOG_NEW_DEFAULT_CATEGORY(dag_scheduling, "Logging specific to this example");
 namespace sg4 = simgrid::s4u;
 
-struct HostAttribute {
-  /* Earliest time at which a host is ready to execute a task */
-  double available_at                     = 0.0;
-  sg4::Exec* last_scheduled_task          = nullptr;
-};
-
-static double sg_host_get_available_at(const sg4::Host* host)
-{
-  return host->get_data<HostAttribute>()->available_at;
-}
-
-static void sg_host_set_available_at(const sg4::Host* host, double time)
-{
-  host->get_data<HostAttribute>()->available_at = time;
-}
-
-static sg4::Exec* sg_host_get_last_scheduled_task(const sg4::Host* host)
-{
-  return host->get_data<HostAttribute>()->last_scheduled_task;
-}
-
-static void sg_host_set_last_scheduled_task(const sg4::Host* host, sg4::ExecPtr task)
-{
-  host->get_data<HostAttribute>()->last_scheduled_task = task.get();
-}
-
-static bool dependency_exists(const sg4::Exec* src, sg4::Exec* dst)
-{
-  const auto& dependencies = src->get_dependencies();
-  const auto& successors   = src->get_successors();
-  return (std::find(successors.begin(), successors.end(), dst) != successors.end() ||
-          dependencies.find(dst) != dependencies.end());
-}
-
 static std::vector<sg4::Exec*> get_ready_tasks(const std::vector<sg4::ActivityPtr>& dax)
 {
   std::vector<sg4::Exec*> ready_tasks;
@@ -71,61 +37,65 @@ static std::vector<sg4::Exec*> get_ready_tasks(const std::vector<sg4::ActivityPt
   return ready_tasks;
 }
 
-static double finish_on_at(const sg4::ExecPtr task, const sg4::Host* host)
+static sg4::Host* get_best_host(const sg4::ExecPtr exec, double* min_finish_time)
 {
-  double data_available      = 0.;
-  double last_data_available = -1.0;
-  /* compute last_data_available */
-  for (const auto& parent : task->get_dependencies()) {
-    /* normal case */
-    if (const auto* comm = dynamic_cast<sg4::Comm*>(parent.get())) {
-      auto source = comm->get_source();
-      XBT_DEBUG("transfer from %s to %s", source->get_cname(), host->get_cname());
-      /* Estimate the redistribution time from this parent */
-      double redist_time;
-      if (comm->get_remaining() <= 1e-6) {
-        redist_time = 0;
-      } else {
-        redist_time =
-            sg_host_get_route_latency(source, host) + comm->get_remaining() / sg_host_get_route_bandwidth(source, host);
+  sg4::Host* best_host = nullptr;
+  *min_finish_time = std::numeric_limits<double>::max();
+
+  for (const auto& host : sg4::Engine::get_instance()->get_all_hosts()) {
+    double data_available      = 0.;
+    double last_data_available = -1.0;
+    /* compute last_data_available */
+    for (const auto& parent : exec->get_dependencies()) {
+      /* normal case */
+      if (const auto* comm = dynamic_cast<sg4::Comm*>(parent.get())) {
+        auto source = comm->get_source();
+        XBT_DEBUG("transfer from %s to %s", source->get_cname(), host->get_cname());
+        /* Estimate the redistribution time from this parent */
+        double redist_time;
+        if (comm->get_remaining() <= 1e-6) {
+          redist_time = 0;
+        } else {
+          double bandwidth      = std::numeric_limits<double>::max();
+          auto [links, latency] = source->route_to(host);
+          for (auto const& link : links)
+            bandwidth = std::min(bandwidth, link->get_bandwidth());
+
+          redist_time = latency + comm->get_remaining() / bandwidth;
+        }
+        // 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;
       }
-      // 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 (last_data_available < data_available)
-      last_data_available = data_available;
-  }
+      /* no transfer, control dependency */
+      if (const auto* parent_exec = dynamic_cast<sg4::Exec*>(parent.get()))
+        data_available = parent_exec->get_finish_time();
 
-  return std::max(sg_host_get_available_at(host), last_data_available) + task->get_remaining() / host->get_speed();
-}
+      if (last_data_available < data_available)
+        last_data_available = data_available;
+    }
 
-static sg4::Host* get_best_host(const sg4::ExecPtr exec)
-{
-  std::vector<sg4::Host*> hosts          = sg4::Engine::get_instance()->get_all_hosts();
-  auto best_host                         = hosts.front();
-  double min_EFT                         = finish_on_at(exec, best_host);
+    double finish_time = std::max(*host->get_data<double>(), last_data_available) +
+                         exec->get_remaining() / host->get_speed();
 
-  for (const auto& h : hosts) {
-    double EFT = finish_on_at(exec, h);
-    XBT_DEBUG("%s finishes on %s at %f", exec->get_cname(), h->get_cname(), EFT);
+    XBT_DEBUG("%s finishes on %s at %f", exec->get_cname(), host->get_cname(), finish_time);
 
-    if (EFT < min_EFT) {
-      min_EFT   = EFT;
-      best_host = h;
+    if (finish_time < *min_finish_time) {
+      *min_finish_time = finish_time;
+      best_host        = host;
     }
   }
+
   return best_host;
 }
 
-static void schedule_on(sg4::ExecPtr exec, sg4::Host* host)
+static void schedule_on(sg4::ExecPtr exec, sg4::Host* host, double busy_until = 0.0)
 {
   exec->set_host(host);
+  // We use the user data field to store up to when the host is busy
+  delete host->get_data<double>(); // In case we're erasing a previous value
+  host->set_data(new double(busy_until));
   // we can also set the destination of all the input comms of this exec
   for (const auto& pred : exec->get_dependencies()) {
     auto* comm = dynamic_cast<sg4::Comm*>(pred.get());
@@ -148,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);
@@ -166,19 +133,21 @@ int main(int argc, char** argv)
 
   e.load_platform(argv[1]);
 
-  /*  Allocating the host attribute */
-  unsigned long total_nhosts = e.get_host_count();
-  const auto hosts          = e.get_all_hosts();
-  std::vector<HostAttribute> host_attributes(total_nhosts);
-  for (unsigned long i = 0; i < total_nhosts; i++)
-    hosts[i]->set_data(&host_attributes[i]);
-
+  /* Mark all hosts as sequential, as it ought to be in such a scheduling example.
+   *
+   * It means that the hosts can only compute one thing at a given time. If an execution already takes place on a given
+   * host, any subsequently started execution will be queued until after the first execution terminates */
+  for (auto const& host : e.get_all_hosts()) {
+    host->set_concurrency_limit(1);
+    host->set_data(new double(0.0));
+  }
   /* load the DAX file */
   auto dax = sg4::create_DAG_from_DAX(argv[2]);
 
   /* Schedule the root first */
+  double root_finish_time;
   auto* root = static_cast<sg4::Exec*>(dax.front().get());
-  auto host  = get_best_host(root);
+  auto* host = get_best_host(root, &root_finish_time);
   schedule_on(root, host);
 
   e.run();
@@ -190,54 +159,40 @@ int main(int argc, char** argv)
     vetoed.clear();
 
     if (ready_tasks.empty()) {
-      /* there is no ready task, let advance the simulation */
+      /* there is no ready exec, let advance the simulation */
       e.run();
       continue;
     }
-    /* For each ready task:
+    /* For each ready exec:
      * get the host that minimizes the completion time.
-     * select the task that has the minimum completion time on its best host.
+     * select the exec that has the minimum completion time on its best host.
      */
-    double min_finish_time            = -1.0;
-    sg4::Exec* selected_task          = nullptr;
-    sg4::Host* selected_host          = nullptr;
-
-    for (auto task : ready_tasks) {
-      XBT_DEBUG("%s is ready", task->get_cname());
-      host               = get_best_host(task);
-      double finish_time = finish_on_at(task, host);
-      if (min_finish_time < 0 || finish_time < min_finish_time) {
+    double min_finish_time   = std::numeric_limits<double>::max();
+    sg4::Exec* selected_task = nullptr;
+    sg4::Host* selected_host = nullptr;
+
+    for (auto exec : ready_tasks) {
+      XBT_DEBUG("%s is ready", exec->get_cname());
+      double finish_time;
+      host = get_best_host(exec, &finish_time);
+      if (finish_time < min_finish_time) {
         min_finish_time = finish_time;
-        selected_task   = task;
+        selected_task   = exec;
         selected_host   = host;
       }
     }
 
     XBT_INFO("Schedule %s on %s", selected_task->get_cname(), selected_host->get_cname());
-    schedule_on(selected_task, selected_host);
-
-    /*
-     * tasks can be executed concurrently when they can by default.
-     * Yet schedulers take decisions assuming that tasks wait for resource availability to start.
-     * The solution (well crude hack is to keep track of the last task scheduled on a host and add a special type of
-     * dependency if needed to force the sequential execution meant by the scheduler.
-     * If the last scheduled task is already done, has failed or is a predecessor of the current task, no need for a
-     * new dependency
-     */
-
-    if (auto last_scheduled_task = sg_host_get_last_scheduled_task(selected_host);
-        last_scheduled_task && (last_scheduled_task->get_state() != sg4::Activity::State::FINISHED) &&
-        (last_scheduled_task->get_state() != sg4::Activity::State::FAILED) &&
-        not dependency_exists(sg_host_get_last_scheduled_task(selected_host), selected_task))
-      last_scheduled_task->add_successor(selected_task);
-
-    sg_host_set_last_scheduled_task(selected_host, selected_task);
-    sg_host_set_available_at(selected_host, min_finish_time);
+    schedule_on(selected_task, selected_host, min_finish_time);
 
     ready_tasks.clear();
     e.run();
   }
 
+  /* Cleanup memory */
+  for (auto const* h : e.get_all_hosts())
+    delete h->get_data<double>();
+
   XBT_INFO("Simulation Time: %f", simgrid_get_clock());
 
   return 0;
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..f473664 100644 (file)
@@ -54,9 +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(),
              (exec.dependencies_solved() ? "solved" : "NOT solved"),
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 cffdd1c..8f2d6cf 100644 (file)
@@ -3,7 +3,7 @@
 ! expect return 2
 ! timeout 30
 ! output display
-$ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true ${bindir:=.}/s4u-mc-bugged1-liveness ${platfdir:=.}/small_platform.xml 1 --log=xbt_cfg.thresh:warning "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --cfg=contexts/factory:ucontext --cfg=model-check/visited:100 --cfg=contexts/stack-size:256  --cfg=model-check/property:promela_bugged1_liveness
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true ${bindir:=.}/s4u-mc-bugged1-liveness ${platfdir:=.}/small_platform.xml 1 --log=xbt_cfg.thresh:warning "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --cfg=contexts/factory:ucontext --cfg=model-check/visited:100 --cfg=contexts/stack-size:256  --cfg=model-check/property:promela_bugged1_liveness
 > [  0.000000] (0:maestro@) Check the liveness property promela_bugged1_liveness
 > [  0.000000] (2:client@Boivin) Ask the request
 > [  0.000000] (3:client@Fafard) Ask the request
index 14da430..58d6358 100644 (file)
@@ -3,7 +3,7 @@
 ! expect return 2
 ! timeout 20
 ! output display
-$ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true ${bindir:=.}/s4u-mc-bugged1-liveness ${platfdir:=.}/small_platform.xml 0 --log=xbt_cfg.thresh:warning "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --cfg=contexts/factory:ucontext --cfg=contexts/stack-size:256 --cfg=model-check/property:promela_bugged1_liveness
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true ${bindir:=.}/s4u-mc-bugged1-liveness ${platfdir:=.}/small_platform.xml 0 --log=xbt_cfg.thresh:warning "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --cfg=contexts/factory:ucontext --cfg=contexts/stack-size:256 --cfg=model-check/property:promela_bugged1_liveness
 > [  0.000000] (0:maestro@) Check the liveness property promela_bugged1_liveness
 > [  0.000000] (2:client@Boivin) Ask the request
 > [  0.000000] (3:client@Fafard) Ask the request
index 0d3b1f4..c04f3f2 100644 (file)
@@ -2,13 +2,61 @@
 
 ! expect return 1
 ! timeout 20
-$ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-bugged1 ${platfdir:=.}/model_checker_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning --cfg=contexts/stack-size:256
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-bugged1 ${platfdir:=.}/model_checker_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning --cfg=contexts/stack-size:256
 > [  0.000000] (0:maestro@) Start a DFS exploration. Reduction is: dpor.
 > [  0.000000] (2:client@HostB) Sent!
 > [  0.000000] (3:client@HostC) Sent!
 > [  0.000000] (1:server@HostA) OK
 > [  0.000000] (4:client@HostD) Sent!
 > [  0.000000] (2:client@HostB) Sent!
+> [  0.000000] (1:server@HostA) OK
+> [  0.000000] (3:client@HostC) Sent!
+> [  0.000000] (4:client@HostD) Sent!
+> [  0.000000] (2:client@HostB) Sent!
+> [  0.000000] (1:server@HostA) OK
+> [  0.000000] (4:client@HostD) Sent!
+> [  0.000000] (3:client@HostC) Sent!
+> [  0.000000] (2:client@HostB) Sent!
+> [  0.000000] (3:client@HostC) Sent!
+> [  0.000000] (1:server@HostA) OK
+> [  0.000000] (4:client@HostD) Sent!
+> [  0.000000] (2:client@HostB) Sent!
+> [  0.000000] (4:client@HostD) Sent!
+> [  0.000000] (1:server@HostA) OK
+> [  0.000000] (3:client@HostC) Sent!
+> [  0.000000] (2:client@HostB) Sent!
+> [  0.000000] (4:client@HostD) Sent!
+> [  0.000000] (3:client@HostC) Sent!
+> [  0.000000] (1:server@HostA) OK
+> [  0.000000] (2:client@HostB) Sent!
+> [  0.000000] (3:client@HostC) Sent!
+> [  0.000000] (1:server@HostA) OK
+> [  0.000000] (4:client@HostD) Sent!
+> [  0.000000] (2:client@HostB) Sent!
+> [  0.000000] (3:client@HostC) Sent!
+> [  0.000000] (1:server@HostA) OK
+> [  0.000000] (4:client@HostD) Sent!
+> [  0.000000] (2:client@HostB) Sent!
+> [  0.000000] (1:server@HostA) OK
+> [  0.000000] (3:client@HostC) Sent!
+> [  0.000000] (4:client@HostD) Sent!
+> [  0.000000] (2:client@HostB) Sent!
+> [  0.000000] (1:server@HostA) OK
+> [  0.000000] (4:client@HostD) Sent!
+> [  0.000000] (3:client@HostC) Sent!
+> [  0.000000] (2:client@HostB) Sent!
+> [  0.000000] (3:client@HostC) Sent!
+> [  0.000000] (1:server@HostA) OK
+> [  0.000000] (4:client@HostD) Sent!
+> [  0.000000] (2:client@HostB) Sent!
+> [  0.000000] (4:client@HostD) Sent!
+> [  0.000000] (1:server@HostA) OK
+> [  0.000000] (3:client@HostC) Sent!
+> [  0.000000] (2:client@HostB) Sent!
+> [  0.000000] (4:client@HostD) Sent!
+> [  0.000000] (3:client@HostC) Sent!
+> [  0.000000] (1:server@HostA) OK
+> [  0.000000] (2:client@HostB) Sent!
 > [  0.000000] (3:client@HostC) Sent!
 > [  0.000000] (1:server@HostA) OK
 > [  0.000000] (4:client@HostD) Sent!
@@ -18,14 +66,14 @@ $ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-bugged1 ${platfdir:=.
 > [  0.000000] (0:maestro@) **************************
 > [  0.000000] (0:maestro@) Counter-example execution trace:
 > [  0.000000] (0:maestro@)   1: iRecv(mbox=0)
-> [  0.000000] (0:maestro@)   4: iSend(mbox=0)
-> [  0.000000] (0:maestro@)   1: WaitComm(from 4 to 1, mbox=0, no timeout)
-> [  0.000000] (0:maestro@)   1: iRecv(mbox=0)
 > [  0.000000] (0:maestro@)   2: iSend(mbox=0)
 > [  0.000000] (0:maestro@)   1: WaitComm(from 2 to 1, mbox=0, no timeout)
 > [  0.000000] (0:maestro@)   1: iRecv(mbox=0)
 > [  0.000000] (0:maestro@)   2: WaitComm(from 2 to 1, mbox=0, no timeout)
+> [  0.000000] (0:maestro@)   4: iSend(mbox=0)
+> [  0.000000] (0:maestro@)   1: WaitComm(from 4 to 1, mbox=0, no timeout)
+> [  0.000000] (0:maestro@)   1: iRecv(mbox=0)
 > [  0.000000] (0:maestro@)   3: iSend(mbox=0)
 > [  0.000000] (0:maestro@)   1: WaitComm(from 3 to 1, mbox=0, no timeout)
-> [  0.000000] (0:maestro@) You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1;4;1;1;2;1;1;2;3;1'
-> [  0.000000] (0:maestro@) DFS exploration ended. 32 unique states visited; 2 backtracks (36 transition replays, 2 states visited overall)
\ No newline at end of file
+> [  0.000000] (0:maestro@) You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1;2;1;1;2;4;1;1;3;1'
+> [  0.000000] (0:maestro@) DFS exploration ended. 59 unique states visited; 14 backtracks (119 transition replays, 192 states visited overall)
\ No newline at end of file
index a48a2ab..28329f6 100644 (file)
@@ -3,4 +3,4 @@
 ! expect return 2
 ! timeout 20
 ! output ignore
-$ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-bugged2-liveness ${platfdir:=.}/small_platform.xml --log=xbt_cfg.thresh:warning "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --cfg=contexts/factory:ucontext --cfg=contexts/stack-size:256 --cfg=model-check/property:promela_bugged2_liveness
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-bugged2-liveness ${platfdir:=.}/small_platform.xml --log=xbt_cfg.thresh:warning "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --cfg=contexts/factory:ucontext --cfg=contexts/stack-size:256 --cfg=model-check/property:promela_bugged2_liveness
index e06f1d0..204b615 100644 (file)
@@ -2,7 +2,7 @@
 
 ! expect return 1
 ! timeout 20
-$ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true ${bindir:=.}/s4u-mc-bugged2 ${platfdir:=.}/model_checker_platform.xml  --log=root.thresh:critical --log=mc.thresh:info
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true ${bindir:=.}/s4u-mc-bugged2 ${platfdir:=.}/model_checker_platform.xml  --log=root.thresh:critical --log=mc.thresh:info
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
 > [0.000000] [mc_explo/INFO] **************************
 > [0.000000] [mc_explo/INFO] *** PROPERTY NOT VALID ***
@@ -11,16 +11,16 @@ $ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true ${bindir
 > [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]   3: 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:'1;3;1;3;1;3;1'
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 742 unique states visited; 145 backtracks (2218 transition replays, 1331 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. 2379 unique states visited; 652 backtracks (7258 transition replays, 10289 states visited overall)
 
 ! expect return 1
 ! timeout 20
-$ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/strategy:nb_wait ${bindir:=.}/s4u-mc-bugged2 ${platfdir:=.}/model_checker_platform.xml  --log=root.thresh:critical --log=mc.thresh:info
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/strategy:min_match_comm ${bindir:=.}/s4u-mc-bugged2 ${platfdir:=.}/model_checker_platform.xml  --log=root.thresh:critical --log=mc.thresh:info
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
 > [0.000000] [mc_explo/INFO] **************************
 > [0.000000] [mc_explo/INFO] *** PROPERTY NOT VALID ***
@@ -32,6 +32,25 @@ $ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=mo
 > [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]   2: 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. 644 unique states visited; 157 backtracks (2475 transition replays, 1674 states visited overall)
\ No newline at end of file
+> [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;2;1'
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 930 unique states visited; 207 backtracks (2195 transition replays, 3332 states visited overall)
+
+! expect return 1
+! timeout 20
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/strategy:max_match_comm ${bindir:=.}/s4u-mc-bugged2 ${platfdir:=.}/model_checker_platform.xml  --log=root.thresh:critical --log=mc.thresh:info
+> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
+> [0.000000] [mc_explo/INFO] **************************
+> [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]   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]   3: WaitComm(from 3 to 1, mbox=0, no timeout)
+> [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] You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1;3;1;3;1;3;1'
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 173 unique states visited; 29 backtracks (286 transition replays, 488 states visited overall)
index 7c75947..6e3c2f0 100644 (file)
@@ -1,6 +1,6 @@
 #!/usr/bin/env tesh
 
-$ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-electric-fence ${platfdir}/model_checker_platform.xml
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-electric-fence ${platfdir}/model_checker_platform.xml
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
@@ -8,18 +8,21 @@ $ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-electric-fence ${plat
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
-> [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
-> [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
-> [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
+> [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
+> [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
+> [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
+> [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
+> [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
+> [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
@@ -29,15 +32,18 @@ $ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-electric-fence ${plat
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
-> [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
+> [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
-> [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
+> [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
+> [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
+> [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
+> [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
@@ -56,17 +62,11 @@ $ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-electric-fence ${plat
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
-> [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
-> [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
-> [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
-> [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
-> [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
-> [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
@@ -80,13 +80,13 @@ $ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-electric-fence ${plat
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
+> [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
+> [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
+> [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
 > [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
 > [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
-> [HostB:client:(2) 0.000000] [electric_fence/INFO] Sent!
-> [HostA:server:(1) 0.000000] [electric_fence/INFO] OK
-> [HostC:client:(3) 0.000000] [electric_fence/INFO] Sent!
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 169 unique states visited; 28 backtracks (261 transition replays, 64 states visited overall)
\ No newline at end of file
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 169 unique states visited; 28 backtracks (64 transition replays, 261 states visited overall)
index 8e0c505..3e9aa79 100644 (file)
@@ -2,7 +2,7 @@
 
 ! expect return 1
 ! timeout 300
-$ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/reduction:none -- ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml --log=root.thresh:critical
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/reduction:none -- ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml --log=root.thresh:critical
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/reduction' to 'none'
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: none.
 > [0.000000] [mc_explo/INFO] **************************
@@ -16,13 +16,53 @@ $ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/reduction:none -- ${bin
 > [0.000000] [mc_explo/INFO]   2: iSend(mbox=0)
 > [0.000000] [mc_explo/INFO]   1: WaitComm(from 2 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;2;1'
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 21 unique states visited; 2 backtracks (24 transition replays, 1 states visited overall)
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 119 unique states visited; 36 backtracks (175 transition replays, 330 states visited overall)
 
 ! expect return 1
 ! timeout 300
-$ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/reduction:none --cfg=model-check/strategy:nb_wait -- ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml --log=root.thresh:critical
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/reduction:none --cfg=model-check/strategy:max_match_comm -- ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml --log=root.thresh:critical
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/reduction' to 'none'
-> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/strategy' to 'nb_wait'
+> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/strategy' to 'max_match_comm'
+> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: none.
+> [0.000000] [mc_explo/INFO] **************************
+> [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]   1: WaitComm(from 3 to 1, mbox=0, no timeout)
+> [0.000000] [mc_explo/INFO]   1: iRecv(mbox=0)
+> [0.000000] [mc_explo/INFO]   2: iSend(mbox=0)
+> [0.000000] [mc_explo/INFO]   1: WaitComm(from 2 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;2;1'
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 29 unique states visited; 5 backtracks (17 transition replays, 51 states visited overall)
+
+
+! expect return 1
+! timeout 300
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/reduction:none --cfg=model-check/strategy:min_match_comm -- ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml --log=root.thresh:critical
+> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/reduction' to 'none'
+> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/strategy' to 'min_match_comm'
+> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: none.
+> [0.000000] [mc_explo/INFO] **************************
+> [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]   1: iRecv(mbox=0)
+> [0.000000] [mc_explo/INFO]   3: iSend(mbox=0)
+> [0.000000] [mc_explo/INFO]   2: 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]   1: WaitComm(from 2 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;2;1;1;1'
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 13 unique states visited; 1 backtracks (1 transition replays, 15 states visited overall)
+
+! expect return 1
+! timeout 300
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/reduction:none --cfg=model-check/strategy:uniform --cfg=model-check/rand-seed:42 -- ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml --log=root.thresh:critical
+> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/reduction' to 'none'
+> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/strategy' to 'uniform'
+> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/rand-seed' to '42'
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: none.
 > [0.000000] [mc_explo/INFO] **************************
 > [0.000000] [mc_explo/INFO] *** PROPERTY NOT VALID ***
@@ -34,7 +74,6 @@ $ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/reduction:none --cfg=mo
 > [0.000000] [mc_explo/INFO]   3: WaitComm(from 3 to 1, mbox=0, no timeout)
 > [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]   2: WaitComm(from 2 to 1, mbox=0, no timeout)
 > [0.000000] [mc_explo/INFO]   1: WaitComm(from 2 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;2;1;3;1;1;2;1'
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 8 unique states visited; 0 backtracks (8 transition replays, 0 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:'3;2;1;3;1;1;1'
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 44 unique states visited; 7 backtracks (19 transition replays, 70 states visited overall)
\ No newline at end of file
index 97c8085..cb54584 100644 (file)
@@ -2,7 +2,7 @@
 
 ! expect return 1
 ! timeout 300
-$ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/visited:10000 -- ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml --log=root.thresh:critical
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/visited:10000 -- ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml --log=root.thresh:critical
 > [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/visited' to '20'
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
 > [0.000000] [mc_ModelChecker/INFO] **************************
index 13890b6..5404b6e 100644 (file)
@@ -2,11 +2,61 @@
 
 ! expect return 1
 ! timeout 20
-$ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning
 > [  0.000000] (0:maestro@) Start a DFS exploration. Reduction is: dpor.
 > [  0.000000] (2:client1@Bourassa) Sent!
 > [  0.000000] (1:server@Boivin) OK
 > [  0.000000] (3:client2@Fafard) Sent!
+> [  0.000000] (1:server@Boivin) OK
+> [  0.000000] (2:client1@Bourassa) Sent!
+> [  0.000000] (3:client2@Fafard) Sent!
+> [  0.000000] (1:server@Boivin) OK
+> [  0.000000] (3:client2@Fafard) Sent!
+> [  0.000000] (2:client1@Bourassa) Sent!
+> [  0.000000] (2:client1@Bourassa) Sent!
+> [  0.000000] (1:server@Boivin) OK
+> [  0.000000] (3:client2@Fafard) Sent!
+> [  0.000000] (2:client1@Bourassa) Sent!
+> [  0.000000] (0:maestro@) **************************
+> [  0.000000] (0:maestro@) *** PROPERTY NOT VALID ***
+> [  0.000000] (0:maestro@) **************************
+> [  0.000000] (0:maestro@) Counter-example execution trace:
+> [  0.000000] (0:maestro@)   1: iRecv(mbox=0)
+> [  0.000000] (0:maestro@)   3: iSend(mbox=0)
+> [  0.000000] (0:maestro@)   1: WaitComm(from 3 to 1, mbox=0, no timeout)
+> [  0.000000] (0:maestro@)   1: iRecv(mbox=0)
+> [  0.000000] (0:maestro@)   2: iSend(mbox=0)
+> [  0.000000] (0:maestro@)   1: WaitComm(from 2 to 1, mbox=0, no timeout)
+> [  0.000000] (0:maestro@) 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;2;1'
+> [  0.000000] (0:maestro@) DFS exploration ended. 26 unique states visited; 6 backtracks (21 transition replays, 53 states visited overall)
+
+! expect return 1
+! timeout 20
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/strategy:min_match_comm ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning
+> [  0.000000] (0:maestro@) Start a DFS exploration. Reduction is: dpor.
+> [  0.000000] (1:server@Boivin) OK
+> [  0.000000] (2:client1@Bourassa) Sent!
+> [  0.000000] (3:client2@Fafard) Sent!
+> [  0.000000] (0:maestro@) **************************
+> [  0.000000] (0:maestro@) *** PROPERTY NOT VALID ***
+> [  0.000000] (0:maestro@) **************************
+> [  0.000000] (0:maestro@) Counter-example execution trace:
+> [  0.000000] (0:maestro@)   1: iRecv(mbox=0)
+> [  0.000000] (0:maestro@)   3: iSend(mbox=0)
+> [  0.000000] (0:maestro@)   2: iSend(mbox=0)
+> [  0.000000] (0:maestro@)   1: WaitComm(from 3 to 1, mbox=0, no timeout)
+> [  0.000000] (0:maestro@)   1: iRecv(mbox=0)
+> [  0.000000] (0:maestro@)   1: WaitComm(from 2 to 1, mbox=0, no timeout)
+> [  0.000000] (0:maestro@) You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1;3;2;1;1;1'
+> [  0.000000] (0:maestro@) DFS exploration ended. 13 unique states visited; 1 backtracks (1 transition replays, 15 states visited overall)
+
+! expect return 1
+! timeout 20
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/strategy:max_match_comm ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning
+> [  0.000000] (0:maestro@) Start a DFS exploration. Reduction is: dpor.
+> [  0.000000] (1:server@Boivin) OK
+> [  0.000000] (2:client1@Bourassa) Sent!
+> [  0.000000] (3:client2@Fafard) Sent!
 > [  0.000000] (0:maestro@) **************************
 > [  0.000000] (0:maestro@) *** PROPERTY NOT VALID ***
 > [  0.000000] (0:maestro@) **************************
@@ -18,4 +68,4 @@ $ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true ${bindir
 > [  0.000000] (0:maestro@)   2: iSend(mbox=0)
 > [  0.000000] (0:maestro@)   1: WaitComm(from 2 to 1, mbox=0, no timeout)
 > [  0.000000] (0:maestro@) 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;2;1'
-> [  0.000000] (0:maestro@) DFS exploration ended. 13 unique states visited; 1 backtracks (15 transition replays, 1 states visited overall)
\ No newline at end of file
+> [  0.000000] (0:maestro@) DFS exploration ended. 13 unique states visited; 1 backtracks (1 transition replays, 15 states visited overall)
\ No newline at end of file
@@ -47,10 +47,22 @@ $ ${bindir:=.}/s4u-network-ns3 ${platfdir}/onelink.xml ${srcdir}/onelink_d.xml -
 > [:maestro(0)] [xbt_cfg/INFO] Configuration change: Set 'network/model' to 'ns-3'
 > [C1:worker(2)] [s4u_test/INFO] FLOW[1] : Receive 10000 bytes from S1 to C1
 
+! output sort
 p Crosstraffic TCP option DISABLED
-! output ignore
-$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/crosstraffic.xml ${srcdir}/crosstraffic_d.xml --cfg=network/model:ns-3 --cfg=network/crosstraffic:0
+$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/crosstraffic.xml ${srcdir}/crosstraffic_d.xml --cfg=network/model:ns-3 --cfg=network/crosstraffic:0 "--log=root.fmt:[%h:%a(%i)]%e[%c/%p]%e%m%n"
+> [:maestro(0)] [xbt_cfg/INFO] Configuration change: Set 'network/model' to 'ns-3'
+> [:maestro(0)] [xbt_cfg/INFO] Configuration change: Set 'network/crosstraffic' to '0'
+> [C1:worker(2)] [s4u_test/INFO] FLOW[1] : Receive 10000 bytes from S1 to C1
+> [C1:worker(4)] [s4u_test/INFO] FLOW[2] : Receive 10000 bytes from S1 to C1
+> [C1:worker(6)] [s4u_test/INFO] FLOW[3] : Receive 10000 bytes from S1 to C1
+> [S1:worker(8)] [s4u_test/INFO] FLOW[4] : Receive 10000 bytes from C1 to S1
 
+! output sort
 p Crosstraffic TCP option ENABLED
-! output ignore
-$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/crosstraffic.xml ${srcdir}/crosstraffic_d.xml --cfg=network/model:ns-3 --cfg=network/crosstraffic:1
+$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/crosstraffic.xml ${srcdir}/crosstraffic_d.xml --cfg=network/model:ns-3 --cfg=network/crosstraffic:1 "--log=root.fmt:[%h:%a(%i)]%e[%c/%p]%e%m%n"
+> [:maestro(0)] [xbt_cfg/INFO] Configuration change: Set 'network/model' to 'ns-3'
+> [:maestro(0)] [xbt_cfg/INFO] Configuration change: Set 'network/crosstraffic' to '1'
+> [C1:worker(2)] [s4u_test/INFO] FLOW[1] : Receive 10000 bytes from S1 to C1
+> [C1:worker(4)] [s4u_test/INFO] FLOW[2] : Receive 10000 bytes from S1 to C1
+> [C1:worker(6)] [s4u_test/INFO] FLOW[3] : Receive 10000 bytes from S1 to C1
+> [S1:worker(8)] [s4u_test/INFO] FLOW[4] : Receive 10000 bytes from C1 to S1
diff --git a/examples/cpp/network-ns3/s4u-network-ns3-timed.tesh b/examples/cpp/network-ns3/s4u-network-ns3-timed.tesh
new file mode 100644 (file)
index 0000000..23ec6c5
--- /dev/null
@@ -0,0 +1,89 @@
+#!/usr/bin/env tesh
+
+p In the ns-3 tests, the timings are valid only with the very latest version of ns3 (the exact values may vary with your ns-3 version).
+
+p 3hosts 2links
+
+$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/small_platform_one_link_routes.xml ${srcdir}/3hosts_2links_d.xml --cfg=network/model:ns-3 "--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'
+> [Jupiter:worker(2) 0.000249] [s4u_test/INFO] FLOW[1] : Receive 100 bytes from Tremblay to Jupiter
+
+p 6hosts 3links
+
+$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/small_platform_one_link_routes.xml ${srcdir}/3links_d.xml --cfg=network/model:ns-3 "--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'
+> [Jupiter:worker(2) 0.000498] [s4u_test/INFO] FLOW[1] : Receive 10000 bytes from Tremblay to Jupiter
+> [Ginette:worker(4) 0.007323] [s4u_test/INFO] FLOW[2] : Receive 10000 bytes from Fafard to Ginette
+> [Lovelace:worker(6) 0.037450] [s4u_test/INFO] FLOW[3] : Receive 10000 bytes from Bourassa to Lovelace
+
+$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/small_platform_one_link_routes.xml ${srcdir}/3links-timer_d.xml --cfg=network/model:ns-3 "--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'
+> [Jupiter:worker(2) 0.000498] [s4u_test/INFO] FLOW[1] : Receive 10000 bytes from Tremblay to Jupiter
+> [Ginette:worker(4) 0.007323] [s4u_test/INFO] FLOW[2] : Receive 10000 bytes from Fafard to Ginette
+> [Lovelace:worker(6) 0.037450] [s4u_test/INFO] FLOW[3] : Receive 10000 bytes from Bourassa to Lovelace
+> [Lovelace:worker(7) 2.037450] [s4u_test/INFO] FLOW[4] : Receive 10000 bytes from Bourassa to Lovelace
+
+p One cluster
+
+$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/cluster_backbone.xml ${srcdir}/one_cluster_d.xml --cfg=network/model:ns-3 "--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'
+> [node-6.simgrid.org:worker(2) 0.021502] [s4u_test/INFO] FLOW[1] : Receive 100 bytes from node-2.simgrid.org to node-6.simgrid.org
+
+p Dogbone
+
+! timeout 10
+$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/dogbone.xml ${srcdir}/dogbone_d.xml --cfg=network/model:ns-3 "--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] [res_ns3/WARNING] Ignoring a route between S1 and C1 of length 3: Only routes of length 1 are considered with ns-3.
+> WARNING: You can ignore this warning if your hosts can still communicate when only considering routes of length 1.
+> WARNING: Remove long routes to avoid this harmless message; subsequent long routes will be silently ignored.
+> [C1:worker(3) 0.120224] [s4u_test/INFO] FLOW[0] : Receive 10000 bytes from S1 to C1
+> [C2:worker(4) 0.120234] [s4u_test/INFO] FLOW[1] : Receive 10000 bytes from S2 to C2
+
+p 2hosts 1link
+
+$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/onelink.xml ${srcdir}/onelink_d.xml --cfg=network/model:ns-3 "--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'
+> [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'
+> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'network/crosstraffic' to '0'
+> [C1:worker:(2) 1.236600] [s4u_test/INFO] FLOW[1] : Receive 10000 bytes from S1 to C1
+> [C1:worker:(4) 2.150800] [s4u_test/INFO] FLOW[2] : Receive 10000 bytes from S1 to C1
+> [C1:worker:(6) 3.197000] [s4u_test/INFO] FLOW[3] : Receive 10000 bytes from S1 to C1
+> [S1:worker:(8) 3.537400] [s4u_test/INFO] FLOW[4] : Receive 10000 bytes from C1 to S1
+
+p Crosstraffic TCP option ENABLED
+$ ${bindir:=.}/s4u-network-ns3 ${platfdir}/crosstraffic.xml ${srcdir}/crosstraffic_d.xml --cfg=network/model:ns-3 --cfg=network/crosstraffic:1
+> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'network/model' to 'ns-3'
+> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'network/crosstraffic' to '1'
+> [C1:worker:(2) 1.236600] [s4u_test/INFO] FLOW[1] : Receive 10000 bytes from S1 to C1
+> [C1:worker:(4) 2.150800] [s4u_test/INFO] FLOW[2] : Receive 10000 bytes from S1 to C1
+> [C1:worker:(6) 3.197000] [s4u_test/INFO] FLOW[3] : Receive 10000 bytes from S1 to C1
+> [S1:worker:(8) 3.537400] [s4u_test/INFO] FLOW[4] : Receive 10000 bytes from C1 to S1
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 8a7311f..2369ec0 100644 (file)
@@ -3,7 +3,7 @@
 p Testing a simple master/worker example application handling failures TCP crosstraffic DISABLED
 
 ! output sort 19
-$ ${bindir:=.}/s4u-platform-failures --log=xbt_cfg.thres:critical --log=no_loc ${platfdir}/small_platform_failures.xml ${srcdir:=.}/s4u-platform-failures_d.xml --cfg=path:${srcdir} --cfg=network/crosstraffic:0 "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=res_cpu.t:verbose
+$ ${bindir:=.}/s4u-platform-failures --log=xbt_cfg.thres:critical --log=no_loc ${platfdir}/small_platform_failures.xml ${srcdir:=.}/s4u-platform-failures_d.xml --cfg=network/crosstraffic:0 "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=res_cpu.t:verbose
 > [  0.000000] (0:maestro@) Cannot launch actor 'worker' on failed host 'Fafard'
 > [  0.000000] (0:maestro@) Starting actor worker(Fafard) failed because its host is turned off.
 > [  0.000000] (1:master@Tremblay) Got 5 workers and 20 tasks to process
@@ -123,7 +123,7 @@ $ ${bindir:=.}/s4u-platform-failures --log=xbt_cfg.thres:critical --log=no_loc $
 p Testing a simple master/worker example application handling failures. TCP crosstraffic ENABLED
 
 ! output sort 19
-$ ${bindir:=.}/s4u-platform-failures --log=xbt_cfg.thres:critical --log=no_loc ${platfdir}/small_platform_failures.xml ${srcdir:=.}/s4u-platform-failures_d.xml --cfg=path:${srcdir} "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=res_cpu.t:verbose
+$ ${bindir:=.}/s4u-platform-failures --log=xbt_cfg.thres:critical --log=no_loc ${platfdir}/small_platform_failures.xml ${srcdir:=.}/s4u-platform-failures_d.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=res_cpu.t:verbose
 > [  0.000000] (0:maestro@) Cannot launch actor 'worker' on failed host 'Fafard'
 > [  0.000000] (0:maestro@) Starting actor worker(Fafard) failed because its host is turned off.
 > [  0.000000] (1:master@Tremblay) Got 5 workers and 20 tasks to process
index 518d0d1..6e8010b 100644 (file)
@@ -1,15 +1,15 @@
 #!/usr/bin/env tesh
 
-$ ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt="[Checker]%e%m%n" -- ${bindir:=.}/s4u-synchro-barrier 1 --log=s4u_test.thres:critical --log=root.fmt="[App%e%e%e%e]%e%m%n"
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt="[Checker]%e%m%n" -- ${bindir:=.}/s4u-synchro-barrier 1 --log=s4u_test.thres:critical --log=root.fmt="[App%e%e%e%e]%e%m%n"
 > [Checker] Start a DFS exploration. Reduction is: dpor.
 > [Checker] Execute 1: BARRIER_ASYNC_LOCK(barrier: 0) (stack depth: 1, state: 1, 0 interleaves)
 > [Checker] Execute 1: BARRIER_WAIT(barrier: 0) (stack depth: 2, state: 2, 0 interleaves)
 > [Checker] 0 actors remain, but none of them need to be interleaved (depth 4).
-> [Checker] Execution came to an end at 1;1;0 (state: 3, depth: 3)
-> [Checker] Backtracking from 1;1;0
-> [Checker] DFS exploration ended. 3 unique states visited; 0 backtracks (3 transition replays, 0 states visited overall)
+> [Checker] Execution came to an end at 1;1 (state: 3, depth: 3)
+> [Checker] Backtracking from 1;1
+> [Checker] DFS exploration ended. 3 unique states visited; 0 backtracks (0 transition replays, 3 states visited overall)
 
-$ ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt="[Checker]%e%m%n" -- ${bindir:=.}/s4u-synchro-barrier 2 --log=s4u_test.thres:critical --log=root.fmt="[App%e%e%e%e]%e%m%n"
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt="[Checker]%e%m%n" -- ${bindir:=.}/s4u-synchro-barrier 2 --log=s4u_test.thres:critical --log=root.fmt="[App%e%e%e%e]%e%m%n"
 > [Checker] Start a DFS exploration. Reduction is: dpor.
 > [Checker] Execute 1: BARRIER_ASYNC_LOCK(barrier: 0) (stack depth: 1, state: 1, 0 interleaves)
 > [Checker] Execute 2: BARRIER_ASYNC_LOCK(barrier: 0) (stack depth: 2, state: 2, 0 interleaves)
@@ -28,8 +28,8 @@ $ ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt
 > [Checker]   BARRIER_ASYNC_LOCK(barrier: 0) (state=1)
 > [Checker]   BARRIER_WAIT(barrier: 0) (state=4)
 > [Checker] 0 actors remain, but none of them need to be interleaved (depth 6).
-> [Checker] Execution came to an end at 1;2;1;2;0 (state: 5, depth: 5)
-> [Checker] Backtracking from 1;2;1;2;0
+> [Checker] Execution came to an end at 1;2;1;2 (state: 5, depth: 5)
+> [Checker] Backtracking from 1;2;1;2
 > [Checker] Execute 2: BARRIER_ASYNC_LOCK(barrier: 0) (stack depth: 1, state: 1, 0 interleaves)
 > [Checker] Execute 1: BARRIER_ASYNC_LOCK(barrier: 0) (stack depth: 2, state: 6, 0 interleaves)
 > [Checker] INDEPENDENT Transitions:
@@ -47,11 +47,11 @@ $ ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt
 > [Checker]   BARRIER_ASYNC_LOCK(barrier: 0) (state=6)
 > [Checker]   BARRIER_WAIT(barrier: 0) (state=8)
 > [Checker] 0 actors remain, but none of them need to be interleaved (depth 6).
-> [Checker] Execution came to an end at 2;1;1;2;0 (state: 9, depth: 5)
-> [Checker] Backtracking from 2;1;1;2;0
-> [Checker] DFS exploration ended. 9 unique states visited; 1 backtracks (10 transition replays, 0 states visited overall)
+> [Checker] Execution came to an end at 2;1;1;2 (state: 9, depth: 5)
+> [Checker] Backtracking from 2;1;1;2
+> [Checker] DFS exploration ended. 9 unique states visited; 1 backtracks (0 transition replays, 10 states visited overall)
 
-$ ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt="[Checker]%e%m%n" -- ${bindir:=.}/s4u-synchro-barrier 3 --log=s4u_test.thres:critical --log=root.fmt="[App%e%e%e%e]%e%m%n"
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt="[Checker]%e%m%n" -- ${bindir:=.}/s4u-synchro-barrier 3 --log=s4u_test.thres:critical --log=root.fmt="[App%e%e%e%e]%e%m%n"
 > [Checker] Start a DFS exploration. Reduction is: dpor.
 > [Checker] Execute 1: BARRIER_ASYNC_LOCK(barrier: 0) (stack depth: 1, state: 1, 0 interleaves)
 > [Checker] Execute 2: BARRIER_ASYNC_LOCK(barrier: 0) (stack depth: 2, state: 2, 0 interleaves)
@@ -87,8 +87,8 @@ $ ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt
 > [Checker]   BARRIER_ASYNC_LOCK(barrier: 0) (state=2)
 > [Checker]   BARRIER_WAIT(barrier: 0) (state=6)
 > [Checker] 0 actors remain, but none of them need to be interleaved (depth 8).
-> [Checker] Execution came to an end at 1;2;3;1;2;3;0 (state: 7, depth: 7)
-> [Checker] Backtracking from 1;2;3;1;2;3;0
+> [Checker] Execution came to an end at 1;2;3;1;2;3 (state: 7, depth: 7)
+> [Checker] Backtracking from 1;2;3;1;2;3
 > [Checker] Execute 3: BARRIER_ASYNC_LOCK(barrier: 0) (stack depth: 2, state: 2, 0 interleaves)
 > [Checker] INDEPENDENT Transitions:
 > [Checker]   BARRIER_ASYNC_LOCK(barrier: 0) (state=1)
@@ -122,6 +122,6 @@ $ ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt
 > [Checker]   BARRIER_ASYNC_LOCK(barrier: 0) (state=8)
 > [Checker]   BARRIER_WAIT(barrier: 0) (state=11)
 > [Checker] 0 actors remain, but none of them need to be interleaved (depth 8).
-> [Checker] Execution came to an end at 1;3;2;1;2;3;0 (state: 12, depth: 7)
-> [Checker] Backtracking from 1;3;2;1;2;3;0
-> [Checker] DFS exploration ended. 12 unique states visited; 1 backtracks (14 transition replays, 1 states visited overall)
\ No newline at end of file
+> [Checker] Execution came to an end at 1;3;2;1;2;3 (state: 12, depth: 7)
+> [Checker] Backtracking from 1;3;2;1;2;3
+> [Checker] DFS exploration ended. 12 unique states visited; 1 backtracks (1 transition replays, 14 states visited overall)
\ No newline at end of file
diff --git a/examples/cpp/synchro-mutex/s4u-mc-synchro-mutex-stateful.tesh b/examples/cpp/synchro-mutex/s4u-mc-synchro-mutex-stateful.tesh
new file mode 100644 (file)
index 0000000..8ca73b7
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/env tesh
+
+p This file tests the cfg=model-check/checkpoint option for DFS explorer
+
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/checkpoint:5 --cfg=model-check/sleep-set:true -- ${bindir:=.}/s4u-synchro-mutex --cfg=actors:2 --log=s4u_test.thres:critical
+> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/checkpoint' to '5'
+> [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. 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 bd187e5..3ce27b1 100644 (file)
@@ -2,7 +2,7 @@
 
 p This file tests the dependencies between MUTEX transitions
 
-$ ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt="[Checker]%e%m%n" -- ${bindir:=.}/s4u-synchro-mutex --cfg=actors:1 --log=s4u_test.thres:critical --log=root.fmt="[App%e%e%e%e]%e%m%n"
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt="[Checker]%e%m%n" -- ${bindir:=.}/s4u-synchro-mutex --cfg=actors:1 --log=s4u_test.thres:critical --log=root.fmt="[App%e%e%e%e]%e%m%n"
 > [App    ] Configuration change: Set 'actors' to '1'
 > [Checker] Start a DFS exploration. Reduction is: dpor.
 > [Checker] Execute 1: MUTEX_ASYNC_LOCK(mutex: 0, owner: 1) (stack depth: 1, state: 1, 0 interleaves)
@@ -27,66 +27,66 @@ $ ${bindir:=.}/../../../bin/simgrid-mc --log=mc_dfs.thres:verbose --log=root.fmt
 > [Checker]   MUTEX_UNLOCK(mutex: 0, owner: -1) (state=3)
 > [Checker]   MUTEX_UNLOCK(mutex: 0, owner: -1) (state=6)
 > [Checker] 0 actors remain, but none of them need to be interleaved (depth 8).
-> [Checker] Execution came to an end at 1;1;1;2;2;2;0 (state: 7, depth: 7)
-> [Checker] Backtracking from 1;1;1;2;2;2;0
-> [Checker] Execute 2: MUTEX_ASYNC_LOCK(mutex: 0, owner: 2) (stack depth: 1, state: 1, 0 interleaves)
-> [Checker] Execute 1: MUTEX_ASYNC_LOCK(mutex: 0, owner: 2) (stack depth: 2, state: 8, 0 interleaves)
-> [Checker] Dependent Transitions:
-> [Checker]   MUTEX_ASYNC_LOCK(mutex: 0, owner: 2) (state=1)
-> [Checker]   MUTEX_ASYNC_LOCK(mutex: 0, owner: 2) (state=8)
-> [Checker] Execute 2: MUTEX_WAIT(mutex: 0, owner: 2) (stack depth: 3, state: 9, 0 interleaves)
-> [Checker] INDEPENDENT Transitions:
-> [Checker]   MUTEX_ASYNC_LOCK(mutex: 0, owner: 2) (state=8)
-> [Checker]   MUTEX_WAIT(mutex: 0, owner: 2) (state=9)
-> [Checker] Execute 2: MUTEX_UNLOCK(mutex: 0, owner: 1) (stack depth: 4, state: 10, 0 interleaves)
-> [Checker] INDEPENDENT Transitions:
-> [Checker]   MUTEX_ASYNC_LOCK(mutex: 0, owner: 2) (state=8)
-> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 1) (state=10)
-> [Checker] Execute 1: MUTEX_WAIT(mutex: 0, owner: 1) (stack depth: 5, state: 11, 0 interleaves)
-> [Checker] Dependent Transitions:
-> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 1) (state=10)
-> [Checker]   MUTEX_WAIT(mutex: 0, owner: 1) (state=11)
-> [Checker] Execute 1: MUTEX_UNLOCK(mutex: 0, owner: -1) (stack depth: 6, state: 12, 0 interleaves)
-> [Checker] Dependent Transitions:
-> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 1) (state=10)
-> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: -1) (state=12)
-> [Checker] 0 actors remain, but none of them need to be interleaved (depth 8).
-> [Checker] Execution came to an end at 2;1;2;2;1;1;0 (state: 13, depth: 7)
-> [Checker] Backtracking from 2;1;2;2;1;1;0
+> [Checker] Execution came to an end at 1;1;1;2;2;2 (state: 7, depth: 7)
+> [Checker] Backtracking from 1;1;1;2;2;2
 > [Checker] Execute 2: MUTEX_ASYNC_LOCK(mutex: 0, owner: 1) (stack depth: 3, state: 3, 0 interleaves)
 > [Checker] INDEPENDENT Transitions:
 > [Checker]   MUTEX_WAIT(mutex: 0, owner: 1) (state=2)
 > [Checker]   MUTEX_ASYNC_LOCK(mutex: 0, owner: 1) (state=3)
-> [Checker] Execute 1: MUTEX_UNLOCK(mutex: 0, owner: 2) (stack depth: 4, state: 14, 0 interleaves)
-> [Checker] INDEPENDENT Transitions:
+> [Checker] Dependent Transitions:
+> [Checker]   MUTEX_ASYNC_LOCK(mutex: 0, owner: 1) (state=1)
 > [Checker]   MUTEX_ASYNC_LOCK(mutex: 0, owner: 1) (state=3)
-> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 2) (state=14)
+> [Checker] Execute 1: MUTEX_UNLOCK(mutex: 0, owner: 2) (stack depth: 4, state: 8, 0 interleaves)
 > [Checker] INDEPENDENT Transitions:
+> [Checker]   MUTEX_ASYNC_LOCK(mutex: 0, owner: 1) (state=3)
+> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 2) (state=8)
+> [Checker] Execute 2: MUTEX_WAIT(mutex: 0, owner: 2) (stack depth: 5, state: 9, 0 interleaves)
+> [Checker] Dependent Transitions:
+> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 2) (state=8)
+> [Checker]   MUTEX_WAIT(mutex: 0, owner: 2) (state=9)
+> [Checker] Execute 2: MUTEX_UNLOCK(mutex: 0, owner: -1) (stack depth: 6, state: 10, 0 interleaves)
+> [Checker] Dependent Transitions:
+> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 2) (state=8)
+> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: -1) (state=10)
+> [Checker] 0 actors remain, but none of them need to be interleaved (depth 8).
+> [Checker] Execution came to an end at 1;1;2;1;2;2 (state: 11, depth: 7)
+> [Checker] Backtracking from 1;1;2;1;2;2
+> [Checker] Execute 2: MUTEX_ASYNC_LOCK(mutex: 0, owner: 2) (stack depth: 1, state: 1, 0 interleaves)
+> [Checker] Execute 1: MUTEX_ASYNC_LOCK(mutex: 0, owner: 2) (stack depth: 2, state: 12, 0 interleaves)
+> [Checker] Dependent Transitions:
 > [Checker]   MUTEX_ASYNC_LOCK(mutex: 0, owner: 2) (state=1)
-> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 2) (state=14)
-> [Checker] Execute 2: MUTEX_WAIT(mutex: 0, owner: 2) (stack depth: 5, state: 15, 0 interleaves)
+> [Checker]   MUTEX_ASYNC_LOCK(mutex: 0, owner: 2) (state=12)
+> [Checker] Execute 2: MUTEX_WAIT(mutex: 0, owner: 2) (stack depth: 3, state: 13, 0 interleaves)
+> [Checker] INDEPENDENT Transitions:
+> [Checker]   MUTEX_ASYNC_LOCK(mutex: 0, owner: 2) (state=12)
+> [Checker]   MUTEX_WAIT(mutex: 0, owner: 2) (state=13)
+> [Checker] Execute 2: MUTEX_UNLOCK(mutex: 0, owner: 1) (stack depth: 4, state: 14, 0 interleaves)
+> [Checker] INDEPENDENT Transitions:
+> [Checker]   MUTEX_ASYNC_LOCK(mutex: 0, owner: 2) (state=12)
+> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 1) (state=14)
+> [Checker] Execute 1: MUTEX_WAIT(mutex: 0, owner: 1) (stack depth: 5, state: 15, 0 interleaves)
 > [Checker] Dependent Transitions:
-> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 2) (state=14)
-> [Checker]   MUTEX_WAIT(mutex: 0, owner: 2) (state=15)
-> [Checker] Execute 2: MUTEX_UNLOCK(mutex: 0, owner: -1) (stack depth: 6, state: 16, 0 interleaves)
+> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 1) (state=14)
+> [Checker]   MUTEX_WAIT(mutex: 0, owner: 1) (state=15)
+> [Checker] Execute 1: MUTEX_UNLOCK(mutex: 0, owner: -1) (stack depth: 6, state: 16, 0 interleaves)
 > [Checker] Dependent Transitions:
-> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 2) (state=14)
+> [Checker]   MUTEX_UNLOCK(mutex: 0, owner: 1) (state=14)
 > [Checker]   MUTEX_UNLOCK(mutex: 0, owner: -1) (state=16)
 > [Checker] 0 actors remain, but none of them need to be interleaved (depth 8).
-> [Checker] Execution came to an end at 1;1;2;1;2;2;0 (state: 17, depth: 7)
-> [Checker] Backtracking from 1;1;2;1;2;2;0
-> [Checker] Backtracking from 1;1;2;1;2;2;0
-> [Checker] DFS exploration ended. 17 unique states visited; 2 backtracks (21 transition replays, 2 states visited overall)
+> [Checker] Execution came to an end at 2;1;2;2;1;1 (state: 17, depth: 7)
+> [Checker] Backtracking from 2;1;2;2;1;1
+> [Checker] DFS exploration ended. 17 unique states visited; 2 backtracks (2 transition replays, 21 states visited overall)
 
-$ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true -- ${bindir:=.}/s4u-synchro-mutex --cfg=actors:2 --log=s4u_test.thres:critical
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true -- ${bindir:=.}/s4u-synchro-mutex --cfg=actors:2 --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 '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 (49 transition replays, 126 states visited overall)
 
-$ ${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
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/strategy:uniform --cfg=model-check/rand-seed:42 -- ${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 'model-check/strategy' to 'uniform'
+> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/rand-seed' to '42'
 > [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. 647 unique states visited; 122 backtracks (871 transition replays, 1640 states visited overall)
\ No newline at end of file
index fb8c9f9..3a82897 100644 (file)
@@ -1,6 +1,6 @@
 #!/usr/bin/env tesh
 
-$ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --log=mc_dfs.thres:info --log=root.fmt="[Checker]%e%m%n" -- ${bindir:=.}/s4u-synchro-semaphore --log=sem_test.thres:critical --log=root.fmt="[App%e%e%e%e]%e%m%n"
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --log=mc_dfs.thres:info --log=root.fmt="[Checker]%e%m%n" -- ${bindir:=.}/s4u-synchro-semaphore --log=sem_test.thres:critical --log=root.fmt="[App%e%e%e%e]%e%m%n"
 > [Checker] Configuration change: Set 'model-check/sleep-set' to 'true'
 > [Checker] Start a DFS exploration. Reduction is: dpor.
-> [Checker] DFS exploration ended. 33 unique states visited; 8 backtracks (125 transition replays, 84 states visited overall)
+> [Checker] DFS exploration ended. 33 unique states visited; 8 backtracks (84 transition replays, 125 states visited overall)
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)
diff --git a/examples/cpp/task-switch-host/s4u-task-switch-host.cpp b/examples/cpp/task-switch-host/s4u-task-switch-host.cpp
new file mode 100644 (file)
index 0000000..27038a3
--- /dev/null
@@ -0,0 +1,78 @@
+/* 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 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:
+ *
+ * comm0 -> exec1 -> comm1
+ *     ↳-> exec2 ->comm2
+ *
+ * With exec1 and exec2 on different hosts.
+ */
+
+#include "simgrid/plugins/task.hpp"
+#include "simgrid/s4u.hpp"
+
+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::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 tasks
+  auto comm0 = simgrid::plugins::CommTask::init("comm0");
+  comm0->set_bytes(1e7);
+  comm0->set_source(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 tasks
+  comm0->add_successor(exec2);
+  exec1->add_successor(comm1);
+  exec2->add_successor(comm2);
+
+  // 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 tasks by adding or removing
+  // successors to comm0
+  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);
+      comm0->add_successor(exec1);
+      comm0->remove_successor(exec2);
+    } else {
+      comm0->set_destination(fafard);
+      comm0->add_successor(exec2);
+      comm0->remove_successor(exec1);
+    }
+    count++;
+  });
+
+  // Enqueue four executions for task comm0
+  comm0->enqueue_execs(4);
+
+  // Start the simulation
+  e.run();
+  return 0;
+}
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
diff --git a/examples/cpp/task-variable-load/s4u-task-variable-load.cpp b/examples/cpp/task-variable-load/s4u-task-variable-load.cpp
new file mode 100644 (file)
index 0000000..ddcea76
--- /dev/null
@@ -0,0 +1,64 @@
+/* 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 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.
+ */
+
+#include "simgrid/plugins/task.hpp"
+#include "simgrid/s4u.hpp"
+
+XBT_LOG_NEW_DEFAULT_CATEGORY(task_variable_load, "Messages specific for this s4u example");
+
+static void variable_load(simgrid::plugins::TaskPtr t)
+{
+  XBT_INFO("--- Small load ---");
+  for (int i = 0; i < 3; i++) {
+    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++) {
+    t->enqueue_execs(1);
+    simgrid::s4u::this_actor::sleep_for(1);
+  }
+}
+
+int main(int argc, char* argv[])
+{
+  simgrid::s4u::Engine e(&argc, argv);
+  e.load_platform(argv[1]);
+  simgrid::plugins::Task::init();
+
+  // Retreive hosts
+  auto tremblay = e.host_by_name("Tremblay");
+  auto jupiter  = e.host_by_name("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 tasks
+  comm->add_successor(exec);
+
+  // 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
+  simgrid::s4u::Actor::create("input", tremblay, variable_load, comm);
+
+  // Start the simulation
+  e.run();
+  return 0;
+}
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)
index 8f7240a..79ee80c 100644 (file)
@@ -101,7 +101,7 @@ def create_hostzone(zone: simgrid.NetZone, coord: typing.List[int], ident: int)
         host = host_zone.create_host(cpu_name, speed).seal()
         # the first CPU is the gateway
         if i == 0:
-            gateway = host
+            gateway = host.netpoint
         # create split-duplex link
         link = host_zone.create_split_duplex_link("link-" + cpu_name, link_bw)
         link.set_latency(link_lat).seal()
@@ -111,7 +111,7 @@ def create_hostzone(zone: simgrid.NetZone, coord: typing.List[int], ident: int)
 
     # seal newly created netzone
     host_zone.seal()
-    return host_zone.netpoint, gateway.netpoint
+    return host_zone.netpoint, gateway
 
 #####################################################################################################
 
index 54bfd27..2ab163e 100644 (file)
@@ -3,7 +3,7 @@
 p Testing a simple master/workers example application handling failures
 
 ! output sort 19
-$ ${pythoncmd:=python3} ${PYTHON_TOOL_OPTIONS:=} ${bindir:=.}/platform-failures.py ${platfdir}/small_platform_failures.xml ${srcdir:=.}/platform-failures_d.xml --log=xbt_cfg.thres:critical --log=no_loc --cfg=path:${srcdir} --cfg=network/crosstraffic:0 "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=res_cpu.t:verbose
+$ ${pythoncmd:=python3} ${PYTHON_TOOL_OPTIONS:=} ${bindir:=.}/platform-failures.py ${platfdir}/small_platform_failures.xml ${srcdir:=.}/platform-failures_d.xml --log=xbt_cfg.thres:critical --log=no_loc --cfg=network/crosstraffic:0 "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=res_cpu.t:verbose
 > [  0.000000] (0:maestro@) Cannot launch actor 'worker' on failed host 'Fafard'
 > [  0.000000] (0:maestro@) Starting actor worker(Fafard) failed because its host is turned off.
 > [  0.000000] (1:master@Tremblay) Got 5 workers and 20 tasks to process
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..b74c411
--- /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()
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..1219906
--- /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..3bd21e6
--- /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..7310584
--- /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 _ in range(3):
+        t.enqueue_execs(1)
+        this_actor.sleep_for(100)
+    this_actor.sleep_for(1000)
+    print('--- Heavy load ---')
+    for _ 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 4065548..c2b3186 100644 (file)
@@ -6,7 +6,7 @@ set(_ampi_test_sources ${CMAKE_CURRENT_SOURCE_DIR}/ampi_test/ampi_test.cpp)
 set(MC_tests bugged1 bugged2 bugged1_liveness only_send_deterministic mutual_exclusion non_termination1
              non_termination2 non_termination3 non_termination4 sendsend)
 foreach(x ${MC_tests})
-  if(NOT SIMGRID_HAVE_MC)
+  if(NOT SIMGRID_HAVE_STATEFUL_MC)
     set(_${x}_disable 1)
   endif()
   set(_${x}_sources ${CMAKE_CURRENT_SOURCE_DIR}/mc/${x}.c)
@@ -91,7 +91,7 @@ set(txt_files     ${txt_files}     ${CMAKE_CURRENT_SOURCE_DIR}/replay/actions0.t
 
 if(enable_smpi)
   # MC is currently broken with threads (deadlock => timeout)
-  if(SIMGRID_HAVE_MC)
+  if(SIMGRID_HAVE_STATEFUL_MC)
     add_dependencies(tests-mc smpimain)
     add_dependencies(tests-mc smpi_only_send_deterministic)
     ADD_TESH(smpi-mc-only-send-determinism --setenv srcdir=${CMAKE_HOME_DIRECTORY}/examples/smpi/mc --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms --cd ${CMAKE_BINARY_DIR}/examples/smpi/mc ${CMAKE_HOME_DIRECTORY}/examples/smpi/mc/only_send_deterministic.tesh)
index 3aedd09..b9f0635 100644 (file)
 #include <mpi.h>
 #include <stdio.h>
 
-void multiply(float* a, float* b, float* c, int istart, int iend, int size);
-void multiply_sampled(float* a, float* b, float* c, int istart, int iend, int size);
-
-
-void multiply(float* a, float* b, float* c, int istart, int iend, int size)
+static void multiply(const float* a, const float* b, float* c, int istart, int iend, int size)
 {
     for (int i = istart; i <= iend; ++i) {
         for (int j = 0; j < size; ++j) {
-            for (int k = 0; k < size; ++k) {
-                c[i*size+j] += a[i*size+k] * b[k*size+j];
-            }
+          float sum = 0.0;
+          for (int k = 0; k < size; ++k) {
+            sum += a[i * size + k] * b[k * size + j];
+          }
+          c[i * size + j] += sum;
         }
     }
 }
 
-void multiply_sampled(float* a, float* b, float* c, int istart, int iend, int size)
+static void multiply_sampled(const float* a, const float* b, float* c, int istart, int iend, int size)
 {
     //for (int i = istart; i <= iend; ++i) {
     SMPI_SAMPLE_GLOBAL (int i = istart, i <= iend, ++i, 10, 0.005){
         for (int j = 0; j < size; ++j) {
-            for (int k = 0; k < size; ++k) {
-                c[i*size+j] += a[i*size+k] * b[k*size+j];
-            }
+          float sum = 0.0;
+          for (int k = 0; k < size; ++k) {
+            sum += a[i * size + k] * b[k * size + j];
+          }
+          c[i * size + j] += sum;
         }
     }
 }
index 280152a..a9c2bd6 100644 (file)
@@ -4,13 +4,13 @@
 
 p Test instrumentation of SMPI
 
-$ ${bindir:=.}/../../../smpi_script/bin/smpirun -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=path:${srcdir:=.}/../msg --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning --cfg=smpi/host-speed:1f -np 8 ${bindir:=.}/smpi_gemm 1000 native
+$ ${bindir:=.}/../../../smpi_script/bin/smpirun -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning --cfg=smpi/host-speed:1f -np 8 ${bindir:=.}/smpi_gemm 1000 native
 > [0.000000] [smpi/INFO] You requested to use 8 ranks, but there is only 5 processes in your hostfile...
 > Matrix Size : 1000x1000
 > Native mode
 > Performance= 220.56 GFlop/s, Time= 9.068 sec, Size= 2000000000 Ops
 
-$ ${bindir:=.}/../../../smpi_script/bin/smpirun -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=path:${srcdir:=.}/../msg --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning --cfg=smpi/host-speed:1f -np 8 ${bindir:=.}/smpi_gemm 1000 sampling
+$ ${bindir:=.}/../../../smpi_script/bin/smpirun -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning --cfg=smpi/host-speed:1f -np 8 ${bindir:=.}/smpi_gemm 1000 sampling
 > [0.000000] [smpi/INFO] You requested to use 8 ranks, but there is only 5 processes in your hostfile...
 > Matrix Size : 1000x1000
 > Sampling mode
index 8728915..c6529ab 100644 (file)
@@ -1,7 +1,7 @@
 #!/usr/bin/env tesh
 
 ! timeout 60
-$ ../../../smpi_script/bin/smpirun -wrapper "${bindir:=.}/../../../bin/simgrid-mc" --log=xbt_cfg.thresh:warning -hostfile ${srcdir:=.}/hostfile_only_send_deterministic  -platform ${srcdir:=.}/../../platforms/cluster_backbone.xml --cfg=model-check/communications-determinism:1 --cfg=smpi/buffering:zero --cfg=smpi/host-speed:1Gf ./smpi_only_send_deterministic
+$ $VALGRIND_NO_LEAK_CHECK ../../../smpi_script/bin/smpirun -wrapper "${bindir:=.}/../../../bin/simgrid-mc" --log=xbt_cfg.thresh:warning -hostfile ${srcdir:=.}/hostfile_only_send_deterministic  -platform ${srcdir:=.}/../../platforms/cluster_backbone.xml --cfg=model-check/communications-determinism:1 --cfg=smpi/buffering:zero --cfg=smpi/host-speed:1Gf ./smpi_only_send_deterministic
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
 > [0.000000] [mc_comm_determinism/INFO] Check communication determinism
 > [0.000000] [mc_comm_determinism/INFO] *******************************************************
@@ -10,4 +10,4 @@ $ ../../../smpi_script/bin/smpirun -wrapper "${bindir:=.}/../../../bin/simgrid-m
 > [0.000000] [mc_comm_determinism/INFO] The recv communications pattern of the actor 0 is different! Different source for communication #1
 > [0.000000] [mc_comm_determinism/INFO] Send-deterministic : Yes
 > [0.000000] [mc_comm_determinism/INFO] Recv-deterministic : No
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 313 unique states visited; 89 backtracks (810 transition replays, 408 states visited overall)
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 262 unique states visited; 73 backtracks (666 transition replays, 331 states visited overall)
index a0b188f..2b6590a 100644 (file)
@@ -2,7 +2,7 @@
 
 p Testing the permissive model
 ! timeout 60
-$ ../../../smpi_script/bin/smpirun -quiet -wrapper "${bindir:=.}/../../../bin/simgrid-mc" -np 2 -platform ${platfdir:=.}/cluster_backbone.xml --cfg=smpi/buffering:infty --log=xbt_cfg.thresh:warning ./smpi_sendsend
+$ $VALGRIND_NO_LEAK_CHECK ../../../smpi_script/bin/smpirun -quiet -wrapper "${bindir:=.}/../../../bin/simgrid-mc" -np 2 -platform ${platfdir:=.}/cluster_backbone.xml --cfg=smpi/buffering:infty --log=xbt_cfg.thresh:warning ./smpi_sendsend
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
 > Sent 0 to rank 1
 > Sent 1 to rank 0
@@ -41,7 +41,7 @@ $ ../../../smpi_script/bin/smpirun -quiet -wrapper "${bindir:=.}/../../../bin/si
 p Testing the paranoid model
 ! timeout 60
 ! expect return 3
-$ ../../../smpi_script/bin/smpirun -quiet -wrapper "${bindir:=.}/../../../bin/simgrid-mc" -np 2 -platform ${platfdir:=.}/cluster_backbone.xml --cfg=smpi/buffering:zero --log=xbt_cfg.thresh:warning ./smpi_sendsend
+$ $VALGRIND_NO_LEAK_CHECK ../../../smpi_script/bin/smpirun -quiet -wrapper "${bindir:=.}/../../../bin/simgrid-mc" -np 2 -platform ${platfdir:=.}/cluster_backbone.xml --cfg=smpi/buffering:zero --log=xbt_cfg.thresh:warning ./smpi_sendsend
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
 > [0.000000] [mc_global/INFO] **************************
 > [0.000000] [mc_global/INFO] *** DEADLOCK DETECTED ***
@@ -53,7 +53,6 @@ $ ../../../smpi_script/bin/smpirun -quiet -wrapper "${bindir:=.}/../../../bin/si
 > [0.000000] [mc_global/INFO] Counter-example execution trace:
 > [0.000000] [mc_global/INFO]   1: iSend(mbox=2)
 > [0.000000] [mc_global/INFO]   2: iSend(mbox=0)
-> [0.000000] [mc_global/INFO]   0: 
-> [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:'1;2;0'
+> [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:'1;2'
 > [0.000000] [mc_dfs/INFO] DFS exploration ended. 3 unique states visited; 0 backtracks (3 transition replays, 0 states visited overall)
 > Execution failed with code 3.
index 9a0cbdc..20a4b07 100644 (file)
@@ -4,7 +4,7 @@
 
 p Test instrumentation of SMPI
 
-$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace -trace-file ${bindir:=.}/smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=path:${srcdir:=.}/../msg --cfg=tracing/smpi/computing:yes --cfg=smpi/simulate-computation:no --cfg=tracing/smpi/sleeping:yes  -np 3 ${bindir:=.}/smpi_trace --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
+$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace -trace-file ${bindir:=.}/smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=tracing/smpi/computing:yes --cfg=smpi/simulate-computation:no --cfg=tracing/smpi/sleeping:yes  -np 3 ${bindir:=.}/smpi_trace --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
 
 ! output sort 19
 $ tail -n +3 ${bindir:=.}/smpi_trace.trace
@@ -1345,10 +1345,10 @@ $ tail -n +3 ${bindir:=.}/smpi_trace.trace
 
 $ rm -f ${bindir:=.}/smpi_trace.trace
 
-$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace -trace-resource -trace-file ${bindir:=.}/smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=path:${srcdir:=.}/../msg --cfg=smpi/host-speed:1f -np 3 ${bindir:=.}/smpi_trace --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
+$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace -trace-resource -trace-file ${bindir:=.}/smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=smpi/host-speed:1f -np 3 ${bindir:=.}/smpi_trace --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
 
 $ rm -f ${bindir:=.}/smpi_trace.trace
 
-$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace --cfg=tracing/smpi/display-sizes:yes --cfg=tracing/smpi/computing:yes --cfg=tracing/smpi/internals:yes -trace-file ${bindir:=.}/smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=path:${srcdir:=.}/../msg --cfg=smpi/host-speed:1f -np 3 ${bindir:=.}/smpi_trace --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
+$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace --cfg=tracing/smpi/display-sizes:yes --cfg=tracing/smpi/computing:yes --cfg=tracing/smpi/internals:yes -trace-file ${bindir:=.}/smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=smpi/host-speed:1f -np 3 ${bindir:=.}/smpi_trace --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
 
 $ rm -f ${bindir:=.}/smpi_trace.trace
index c0cd72d..541e177 100644 (file)
@@ -3,19 +3,19 @@
 # Go for the first test
 
 p SMPI test
-$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace -trace-resource -trace-file smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=path:${srcdir:=.}/../msg -np 3 ${bindir:=.}/smpi_trace_simple --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
+$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace -trace-resource -trace-file smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml -np 3 ${bindir:=.}/smpi_trace_simple --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
 
 p Another SMPI test, with only -trace
-$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace -trace-file smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=path:${srcdir:=.}/../msg -np 3 ${bindir:=.}/smpi_trace_simple --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
+$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace -trace-file smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml -np 3 ${bindir:=.}/smpi_trace_simple --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
 
 p Testing without trace parameters
-$ ${bindir:=.}/../../../smpi_script/bin/smpirun -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=path:${srcdir:=.}/../msg -np 3 ${bindir:=.}//smpi_trace_simple --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
+$ ${bindir:=.}/../../../smpi_script/bin/smpirun -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml -np 3 ${bindir:=.}/smpi_trace_simple --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
 
 p Testing grouped tracing
-$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace -trace-grouped -trace-file smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=path:${srcdir:=.}/../msg -np 3 ${bindir:=.}/smpi_trace_simple --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
+$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace -trace-grouped -trace-file smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml -np 3 ${bindir:=.}/smpi_trace_simple --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
 
 
 p Testing with parameters but without activating them with the safe switch (-trace)
-$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace-resource -trace-file smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml --cfg=path:${srcdir:=.}/../msg -np 3 ${bindir:=.}/smpi_trace_simple --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
+$ ${bindir:=.}/../../../smpi_script/bin/smpirun -trace-resource -trace-file smpi_trace.trace -hostfile ${srcdir:=.}/../hostfile -platform ${platfdir:=.}/small_platform.xml -np 3 ${bindir:=.}/smpi_trace_simple --log=smpi_config.thres:warning --log=xbt_cfg.thres:warning
 
 $ rm -f smpi_trace.trace
index 3159ccc..98811c2 100644 (file)
@@ -12,7 +12,9 @@ foreach(x
     add_executable       (pthread-${x} EXCLUDE_FROM_ALL pthread-${x}.c)
     set_target_properties(pthread-${x} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
     target_link_libraries(pthread-${x} PRIVATE Threads::Threads)
-    target_link_libraries(pthread-${x} PUBLIC "-Wl,-znorelro -Wl,-znoseparate-code") # TODO: convert to target_link_option once CMAKE_VERSION is >3.13
+    if(SIMGRID_HAVE_STATEFUL_MC) # Only needed to introspect the binary
+      target_link_libraries(pthread-${x} PUBLIC "-Wl,-znorelro -Wl,-znoseparate-code") # TODO: convert to target_link_option once CMAKE_VERSION is >3.13
+    endif()
 
     add_dependencies(tests pthread-${x})
     ADD_TESH_FACTORIES(pthread-${x} "^thread" --setenv libdir=${CMAKE_BINARY_DIR}/lib --cd ${CMAKE_BINARY_DIR}/examples/sthread ${CMAKE_CURRENT_SOURCE_DIR}/pthread-${x}.tesh)
@@ -35,15 +37,19 @@ endforeach()
 foreach(x
         mutex-simpledeadlock)
 
-  if(SIMGRID_HAVE_MC AND ("${CMAKE_SYSTEM}" MATCHES "Linux")) # sthread is linux-only
+  if("${CMAKE_SYSTEM}" MATCHES "Linux") # sthread is linux-only
 
     add_executable       (pthread-${x} EXCLUDE_FROM_ALL pthread-${x}.c)
     set_target_properties(pthread-${x} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
     target_link_libraries(pthread-${x} PRIVATE Threads::Threads)
-    target_link_libraries(pthread-${x} PUBLIC "-Wl,-znorelro -Wl,-znoseparate-code") # TODO: convert to target_link_option once CMAKE_VERSION is >3.13
+    if(SIMGRID_HAVE_STATEFUL_MC) # Only needed to introspect the binary
+      target_link_libraries(pthread-${x} PUBLIC "-Wl,-znorelro -Wl,-znoseparate-code") # TODO: convert to target_link_option once CMAKE_VERSION is >3.13
+    endif()
 
-    add_dependencies(tests-mc pthread-${x})
-    ADD_TESH_FACTORIES(pthread-mc-${x} "^thread" --setenv libdir=${CMAKE_BINARY_DIR}/lib --cd ${CMAKE_BINARY_DIR}/examples/sthread ${CMAKE_CURRENT_SOURCE_DIR}/pthread-mc-${x}.tesh)
+    if(SIMGRID_HAVE_MC)
+      add_dependencies(tests-mc pthread-${x})
+      ADD_TESH_FACTORIES(pthread-mc-${x} "^thread" --setenv libdir=${CMAKE_BINARY_DIR}/lib --cd ${CMAKE_BINARY_DIR}/examples/sthread ${CMAKE_CURRENT_SOURCE_DIR}/pthread-mc-${x}.tesh)
+    endif()
   endif()
 
   set(tesh_files    ${tesh_files}    ${CMAKE_CURRENT_SOURCE_DIR}/pthread-mc-${x}.tesh)
@@ -56,16 +62,20 @@ endforeach()
 foreach(example
         stdobject)
 
-  if(SIMGRID_HAVE_MC AND ("${CMAKE_SYSTEM}" MATCHES "Linux")) # sthread is linux-only
+  if("${CMAKE_SYSTEM}" MATCHES "Linux") # sthread is linux-only
 
     add_executable       (${example} EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/${example}/${example}.cpp)
     set_target_properties(${example} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
     target_link_libraries(${example} PRIVATE Threads::Threads)
-    target_link_libraries(${example} PUBLIC "-fPIC -Wl,-znorelro -Wl,-znoseparate-code") # TODO: convert to target_link_option once CMAKE_VERSION is >3.13
+    if(SIMGRID_HAVE_STATEFUL_MC) # Only needed to introspect the binary
+      target_link_libraries(${example} PUBLIC "-fPIC -Wl,-znorelro -Wl,-znoseparate-code") # TODO: convert to target_link_option once CMAKE_VERSION is >3.13
+    endif()
     set_target_properties(${example} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${example})
 
-    add_dependencies(tests-mc ${example})
-    ADD_TESH_FACTORIES(sthread-mc-${example} "^thread" --setenv libdir=${CMAKE_BINARY_DIR}/lib --cd ${CMAKE_BINARY_DIR}/examples/sthread/${example} ${CMAKE_CURRENT_SOURCE_DIR}/${example}/${example}.tesh)
+    if(SIMGRID_HAVE_MC)
+      add_dependencies(tests-mc ${example})
+      ADD_TESH_FACTORIES(sthread-mc-${example} "^thread" --setenv libdir=${CMAKE_BINARY_DIR}/lib --cd ${CMAKE_BINARY_DIR}/examples/sthread/${example} ${CMAKE_CURRENT_SOURCE_DIR}/${example}/${example}.tesh)
+    endif()
   endif()
 
   set(tesh_files    ${tesh_files}    ${CMAKE_CURRENT_SOURCE_DIR}/${example}/${example}.tesh)
index 2e15674..6e5bc5f 100644 (file)
@@ -2,17 +2,17 @@
 # We ignore the LD_PRELOAD lines from the expected output because they contain the build path
 ! ignore .*LD_PRELOAD.*
 
-$ ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-mutex-simple
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-mutex-simple
 > [0.000000] [sthread/INFO] Starting the simulation.
 > All threads are started.
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
 > 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
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 23 unique states visited; 2 backtracks (2 transition replays, 27 states visited overall)
\ No newline at end of file
index 5e1f723..5e6cac0 100644 (file)
@@ -5,13 +5,19 @@
 # We ignore the LD_PRELOAD lines from the expected output because they contain the build path
 ! ignore .*LD_PRELOAD.*
 
-$ ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-mutex-simpledeadlock
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-mutex-simpledeadlock
 > [0.000000] [sthread/INFO] Starting the simulation.
 > All threads are started.
 > [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
 > 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.
+> 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] **************************
@@ -27,6 +33,5 @@ $ ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir
 > [0.000000] [mc_global/INFO]   2: MUTEX_ASYNC_LOCK(mutex: 1, owner: 3)
 > [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_global/INFO]   0: 
-> [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'
-> [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_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. 38 unique states visited; 3 backtracks (11 transition replays, 52 states visited overall)
\ No newline at end of file
index db487ac..94c7cb7 100644 (file)
@@ -1,15 +1,16 @@
 # We ignore the LD_PRELOAD lines from the expected output because they contain the build path
 ! ignore .*LD_PRELOAD.*
 
-$ ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/sleep-set:true --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
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/sleep-set:true --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] [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. 1100 unique states visited; 135 backtracks (2937 transition replays, 1702 states visited overall)
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 103 unique states visited; 16 backtracks (163 transition replays, 282 states visited overall)
 
-$ ${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
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/strategy:uniform --cfg=model-check/rand-seed:42 --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] [xbt_cfg/INFO] Configuration change: Set 'model-check/strategy' to 'uniform'
+> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/rand-seed' to '42'
 > [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. 91 unique states visited; 11 backtracks (116 transition replays, 218 states visited overall)
\ No newline at end of file
index 111458a..da50b77 100644 (file)
@@ -4,7 +4,7 @@
 # We ignore the LD_PRELOAD lines from the expected output because they contain the build path
 ! ignore .*LD_PRELOAD.*
 
-$ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/stdobject "--log=root.fmt:[%11.6r]%e(%a@%h)%e%m%n" --log=no_loc
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/stdobject "--log=root.fmt:[%11.6r]%e(%a@%h)%e%m%n" --log=no_loc
 > [   0.000000] (maestro@) Configuration change: Set 'model-check/sleep-set' to 'true'
 > [   0.000000] (maestro@) Starting the simulation.
 > starting two helpers...
@@ -25,4 +25,4 @@ $ ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=mo
 > [   0.000000] (maestro@)   2: BeginObjectAccess(&v)
 > [   0.000000] (maestro@)   3: BeginObjectAccess(&v)
 > [   0.000000] (maestro@) You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'2;3'
-> [   0.000000] (maestro@) DFS exploration ended. 7 unique states visited; 1 backtracks (9 transition replays, 1 states visited overall)
+> [   0.000000] (maestro@) DFS exploration ended. 7 unique states visited; 1 backtracks (1 transition replays, 9 states visited overall)
index 6ef3fd7..bb4f27e 100644 (file)
@@ -13,6 +13,8 @@
 #cmakedefine01 SIMGRID_HAVE_MALLOCATOR
 /* Was the model-checking compiled in? */
 #cmakedefine01 SIMGRID_HAVE_MC
+/* Was the stateful model-checking compiled in? */
+#cmakedefine01 SIMGRID_HAVE_STATEFUL_MC
 /* Was the ns-3 support compiled in? */
 #cmakedefine01 SIMGRID_HAVE_NS3
 #cmakedefine NS3_MINOR_VERSION @NS3_MINOR_VERSION@
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/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 bc02713..3c6a4eb 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,14 +127,19 @@ public:
     } else {
       if (vetoed_activities_ != nullptr)
         vetoed_activities_->insert(this);
-      on_veto(*this);
+      fire_on_veto();
+      fire_on_this_veto();
     }
   }
 
   void complete(Activity::State state)
   {
+    // Ensure that the current activity remains alive until the end of the function, even if its last reference is
+    // 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();
   }
@@ -237,7 +237,41 @@ 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);
@@ -248,7 +282,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);
@@ -256,9 +290,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);
   }
@@ -286,7 +321,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 1f8c31d..8a09719 100644 (file)
@@ -79,6 +79,13 @@ public:
   Disk* set_state_profile(kernel::profile::Profile* profile);
   Disk* set_read_bandwidth_profile(kernel::profile::Profile* profile);
   Disk* set_write_bandwidth_profile(kernel::profile::Profile* profile);
+  /**
+   * @brief Set the max amount of operations (either read or write) that can take place on this disk at the same time
+   *
+   * Use -1 to set no limit.
+   */
+  Disk* set_concurrency_limit(int limit);
+  int get_concurrency_limit() const;
 
   IoPtr io_init(sg_size_t size, s4u::Io::OpType type) const;
 
@@ -126,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 b13b561..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 */
@@ -133,7 +172,15 @@ public:
   Host* set_state_profile(kernel::profile::Profile* p);
   Host* set_speed_profile(kernel::profile::Profile* p);
 
-  /** @brief Convert the CPU's speed from string to double */
+  /**
+   * @brief Set the max amount of executions that can take place on this host at the same time
+   *
+   * Use -1 to set no limit.
+   */
+  Host* set_concurrency_limit(int limit);
+  int get_concurrency_limit() const;
+
+  /** \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
@@ -231,6 +278,7 @@ public:
 
   void route_to(const Host* dest, std::vector<Link*>& links, double* latency) const;
   void route_to(const Host* dest, std::vector<kernel::resource::StandardLinkImpl*>& links, double* latency) const;
+  std::pair<std::vector<Link*>, double> route_to(const Host* dest) const;
 
   /**
    * @brief Seal this host
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 2195c69..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);
 
@@ -130,7 +130,14 @@ public:
   void set_host_wifi_rate(const s4u::Host* host, int level) const;
 
   /** @brief Returns the current load (in bytes per second) */
-  double get_usage() const;
+  double get_load() const;
+
+#ifndef DOXYGEN
+  XBT_ATTRIB_DEPRECATED_v337("Please use get_load() instead") double get_usage() const
+  {
+    return get_load();
+  }
+#endif
 
   /** @brief Check if the Link is used (at least one flow uses the link) */
   bool is_used() const;
@@ -150,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);
+  }
 };
 
 /**
@@ -190,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 2c0b849..5d61fb0 100644 (file)
 
 #include <sys/time.h> /* Load it before the define next line to not mess with the system headers */
 
-#if SIMGRID_HAVE_MC
 #undef assert
 #define assert(x) MC_assert(!!(x))
-#endif
 
 #ifdef TRACE_CALL_LOCATION /* Defined by smpicc on the command line */
 #include <smpi/smpi_extended_traces.h>
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 8071dbd..bb5ee91 100644 (file)
@@ -22,8 +22,7 @@
 #include <xbt/sysdep.h>
 #include <xbt/utility.hpp>
 
-namespace simgrid {
-namespace config {
+namespace simgrid::config {
 
 class Config;
 
@@ -92,10 +91,10 @@ XBT_PUBLIC void alias(const char* realname, std::initializer_list<const char*> a
  */
 template <class T>
 XBT_PUBLIC void declare_flag(const std::string& name, const std::string& description, T value,
-                             std::function<void(const T&)> callback = std::function<void(const T&)>());
+                             std::function<void(const T&)> callback = nullptr);
 template <class T>
 void declare_flag(const std::string& name, std::initializer_list<const char*> aliases, const std::string& description,
-                  T value, std::function<void(const T&)> callback = std::function<void(const T&)>())
+                  T value, std::function<void(const T&)> callback = nullptr)
 {
   declare_flag(name, description, std::move(value), std::move(callback));
   alias(name.c_str(), aliases);
@@ -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) {
@@ -261,6 +258,17 @@ public:
   /* A constructor accepting a map of valid values -> their description,
    * and producing an informative error message when an invalid value is passed, or when help is passed as a value.
    */
+  Flag(const char* name, const char* desc, xbt::type_identity_t<T> value,
+       const std::map<std::string, std::string, std::less<>>& valid_values)
+      : value_(value), name_(name)
+  {
+    simgrid::config::bind_flag(value_, name, desc, valid_values, [](const std::string&) {});
+  }
+
+  /* As earlier, a constructor accepting a map of valid values -> their description,
+   * and producing an informative error message when an invalid value is passed, or when help is passed as a value.
+   * But also take a callback that is invoked before the verification of parameter name validity.
+   */
   template <class F>
   Flag(const char* name, const char* desc, xbt::type_identity_t<T> value,
        const std::map<std::string, std::string, std::less<>>& valid_values, F callback)
@@ -314,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..d0ce338 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_p = cb.ptr()](Task* op) {
+              const py::gil_scoped_acquire py_context; // need a new context for callback
+              py::reinterpret_borrow<py::function>(cb_p)(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_p = cb.ptr()](Task* op) {
+              const py::gil_scoped_acquire py_context; // need a new context for callback
+              py::reinterpret_borrow<py::function>(cb_p)(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 d4d99a2..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
@@ -95,20 +100,25 @@ std::vector<ActivityPtr> create_DAG_from_json(const std::string& filename)
   std::ifstream f(filename);
   auto data = nlohmann::json::parse(f);
   std::vector<ActivityPtr> dag = {};
-  std::map<std::string, std::vector<ActivityPtr>> successors = {};
+  std::map<std::string, std::vector<ActivityPtr>, std::less<>> successors = {};
   std::map<ActivityPtr, Host*> comms_destinations = {};
   ActivityPtr current; 
   
   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) {
@@ -129,12 +139,8 @@ std::vector<ActivityPtr> create_DAG_from_json(const std::string& filename)
     }
 
     dag.push_back(current);
-    for (auto const& parent: task["parents"]) {
-      auto it = successors.find(parent);
-      if (it == successors.end())
-        successors[parent] = {};
-      successors[parent].push_back(current);
-    }
+    for (auto const& parent : task["parents"])
+      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 e13c6ff..b2ecf6e 100644 (file)
@@ -95,3 +95,5 @@
 /* The boost_stacktrace_backtrace library */
 #cmakedefine01 HAVE_BOOST_STACKTRACE_BACKTRACE /* preferred */
 #cmakedefine01 HAVE_BOOST_STACKTRACE_ADDR2LINE /* fallback */
+/* Whether the ns-3 library has GetNextEventTime */
+#cmakedefine01 SIMGRID_HAVE_NS3_GetNextEventTime
index f7af9b4..f4a3d74 100644 (file)
 #include "src/kernel/resource/profile/Profile.hpp"
 #include "src/kernel/xml/platf.hpp"
 #include "src/mc/mc.h"
+#include "src/mc/mc_config.hpp"
 #include "src/mc/mc_record.hpp"
 #include "src/mc/mc_replay.hpp"
 #include "src/simgrid/math_utils.h"
 #include "src/simgrid/sg_config.hpp"
 #include "src/smpi/include/smpi_actor.hpp"
 
+#if SIMGRID_HAVE_MC
+#include "src/mc/remote/AppSide.hpp"
+#endif
+
 #include "xbt/log.hpp"
 
 #include <boost/algorithm/string/predicate.hpp>
 #include <dlfcn.h>
 
-#if SIMGRID_HAVE_MC
-#include "src/mc/remote/AppSide.hpp"
-#endif
-
 XBT_LOG_NEW_DEFAULT_CATEGORY(ker_engine, "Logging specific to Engine (kernel)");
 
 namespace simgrid::kernel {
@@ -175,7 +176,7 @@ void EngineImpl::initialize(int* argc, char** argv)
   // The communication initialization is done ASAP, as we need to get some init parameters from the MC for different
   // layers. But instance_ needs to be created, as we send the address of some of its fields to the MC that wants to
   // read them directly.
-  simgrid::mc::AppSide::initialize();
+  simgrid::mc::AppSide::get(); // To ensure that it's initialized
 #endif
 
   if (static bool inited = false; not inited) {
@@ -193,9 +194,6 @@ void EngineImpl::initialize(int* argc, char** argv)
 
   install_signal_handlers();
 
-  /* register a function to be called after the environment creation */
-  s4u::Engine::on_platform_created_cb([this]() { this->presolve(); });
-
   if (cfg_dbg_clean_atexit)
     atexit(shutdown);
 }
@@ -323,7 +321,7 @@ void EngineImpl::load_deployment(const std::string& file) const
   sg_platf_parser_finalize();
 
   simgrid_parse_open(file);
-  simgrid_parse();
+  simgrid_parse(false);
   simgrid_parse_close();
 }
 
@@ -358,24 +356,27 @@ void EngineImpl::handle_ended_actions() const
     while (auto* action = model->extract_failed_action()) {
       XBT_DEBUG("   Handling Action %p", action);
       if (action->get_activity() != nullptr) { // Skip vcpu actions
+        activity::ActivityImplPtr activity(action->get_activity());
         // Action failures are not automatically reported when the action is started by maestro (as in SimDAG)
-        if (action->get_activity()->get_actor() == maestro_)
-          action->get_activity()->get_iface()->complete(s4u::Activity::State::FAILED);
+        if (activity->get_actor() == maestro_)
+          activity->get_iface()->complete(s4u::Activity::State::FAILED);
 
-        activity::ActivityImplPtr(action->get_activity())->finish();
+        activity->finish();
       }
     }
     XBT_DEBUG("Handling the terminated actions (if any)");
     while (auto* action = model->extract_done_action()) {
       XBT_DEBUG("   Handling Action %p", action);
       if (action->get_activity() != nullptr) {
+        activity::ActivityImplPtr activity(action->get_activity());
+
         // Action termination are not automatically reported when the action is started by maestro (as in SimDAG)
-        action->get_activity()->set_finish_time(action->get_finish_time());
+        activity->set_finish_time(action->get_finish_time());
 
-        if (action->get_activity()->get_actor() == maestro_)
-          action->get_activity()->get_iface()->complete(s4u::Activity::State::FINISHED);
+        if (activity->get_actor() == maestro_)
+          activity->get_iface()->complete(s4u::Activity::State::FINISHED);
 
-        activity::ActivityImplPtr(action->get_activity())->finish();
+        activity->finish();
       }
     }
   }
@@ -476,27 +477,6 @@ void EngineImpl::display_all_actor_status() const
   }
 }
 
-void EngineImpl::presolve() const
-{
-  XBT_DEBUG("Consume all trace events occurring before the starting time.");
-  double next_event_date;
-  while ((next_event_date = profile::future_evt_set.next_date()) != -1.0) {
-    if (next_event_date > now_)
-      break;
-
-    double value                 = -1.0;
-    resource::Resource* resource = nullptr;
-    while (auto* event = profile::future_evt_set.pop_leq(next_event_date, &value, &resource)) {
-      if (value >= 0)
-        resource->apply_event(event, value);
-    }
-  }
-
-  XBT_DEBUG("Set every models in the right state by updating them to 0.");
-  for (auto const& model : models_)
-    model->update_actions_state(now_, 0.0);
-}
-
 double EngineImpl::solve(double max_date) const
 {
   double time_delta            = -1.0; /* duration */
@@ -511,9 +491,9 @@ double EngineImpl::solve(double max_date) const
 
   XBT_DEBUG("Looking for next event in all models");
   for (auto model : models_) {
-    if (not model->next_occurring_event_is_idempotent()) {
+    if (not model->next_occurring_event_is_idempotent())
       continue;
-    }
+
     double next_event = model->next_occurring_event(now_);
     if ((time_delta < 0.0 || next_event < time_delta) && next_event >= 0.0) {
       time_delta = next_event;
@@ -604,6 +584,9 @@ void EngineImpl::run(double max_date)
 {
   seal_platform();
 
+  XBT_DEBUG("Running the main loop until t=%.3f in mode %s", max_date,
+            to_c_str(simgrid::mc::get_model_checking_mode()));
+
   if (MC_is_active()) {
 #if SIMGRID_HAVE_MC
     mc::AppSide::get()->main_loop();
index 6862409..33a12dd 100644 (file)
@@ -152,10 +152,6 @@ public:
   void display_all_actor_status() const;
   void run_all_actors();
 
-  /*  @brief Finish simulation initialization
-   *  This function must be called before the first call to solve()
-   */
-  void presolve() const;
   /** @brief Performs a part of the simulation
    *  @param max_date Maximum date to update the simulation to, or -1
    *  @return the elapsed time, or -1.0 if no event could be executed
index f425dc8..69a6936 100644 (file)
@@ -176,11 +176,10 @@ void ActivityImpl::wait_any_for(actor::ActorImpl* issuer, const std::vector<Acti
 
 void ActivityImpl::suspend()
 {
-  if (model_action_ == nullptr)
-    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 +187,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..7452071 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,12 @@ 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());
+    set_iface(nullptr); // reset iface to protect against multiple trigger of the on_completion signals
+    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 7758425..b631a5e 100644 (file)
@@ -23,7 +23,6 @@ public:
       : ResultingSimcall(actor, true), activity_(activity)
   {
   }
-  bool is_visible() const override { return true; }
   activity::ActivityImpl* get_activity() const { return activity_; }
   void serialize(std::stringstream& stream) const override;
   std::string to_string() const override;
@@ -36,7 +35,6 @@ class ActivityTestanySimcall final : public ResultingSimcall<ssize_t> {
 
 public:
   ActivityTestanySimcall(ActorImpl* actor, const std::vector<activity::ActivityImpl*>& activities);
-  bool is_visible() const override { return true; }
   bool is_enabled() override { return true; /* can return -1 if no activity is ready */ }
   void serialize(std::stringstream& stream) const override;
   std::string to_string() const override;
@@ -57,7 +55,6 @@ public:
   }
   void serialize(std::stringstream& stream) const override;
   std::string to_string() const override;
-  bool is_visible() const override { return true; }
   bool is_enabled() override;
   activity::ActivityImpl* get_activity() const { return activity_; }
   void set_activity(activity::ActivityImpl* activity) { activity_ = activity; }
@@ -75,7 +72,6 @@ public:
   bool is_enabled() override;
   void serialize(std::stringstream& stream) const override;
   std::string to_string() const override;
-  bool is_visible() const override { return true; }
   void prepare(int times_considered) override;
   int get_max_consider() const override;
   const std::vector<activity::ActivityImpl*>& get_activities() const { return activities_; }
@@ -121,7 +117,6 @@ public:
   }
   void serialize(std::stringstream& stream) const override;
   std::string to_string() const override;
-  bool is_visible() const override { return true; }
   activity::MailboxImpl* get_mailbox() const { return mbox_; }
   double get_payload_size() const { return payload_size_; }
   double get_rate() const { return rate_; }
@@ -166,7 +161,6 @@ public:
   }
   void serialize(std::stringstream& stream) const override;
   std::string to_string() const override;
-  bool is_visible() const override { return true; }
   activity::MailboxImpl* get_mailbox() const { return mbox_; }
   double get_rate() const { return rate_; }
   unsigned char* get_dst_buff() const { return dst_buff_; }
index d34a22d..87605e4 100644 (file)
@@ -4,17 +4,14 @@
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
 #include "src/kernel/actor/Simcall.hpp"
+#include "simgrid/modelchecker.h"
 #include "simgrid/s4u/Host.hpp"
 #include "src/kernel/EngineImpl.hpp"
 #include "src/kernel/actor/ActorImpl.hpp"
 #include "src/kernel/actor/SimcallObserver.hpp"
 #include "src/kernel/context/Context.hpp"
-#include "xbt/log.h"
-
-#if SIMGRID_HAVE_MC
-#include "simgrid/modelchecker.h"
 #include "src/mc/mc_replay.hpp"
-#endif
+#include "xbt/log.h"
 
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(ker_simcall, kernel, "transmuting from user request into kernel handlers");
 
@@ -82,11 +79,7 @@ void simcall_run_object_access(std::function<void()> const& code, simgrid::kerne
   // We only need a simcall if the order of the setters is important (parallel run or MC execution).
   // Otherwise, just call the function with no simcall
 
-  if (simgrid::kernel::context::Context::is_parallel()
-#if SIMGRID_HAVE_MC
-      || MC_is_active() || MC_record_replay_is_active()
-#endif
-  ) {
+  if (simgrid::kernel::context::Context::is_parallel() || MC_is_active() || MC_record_replay_is_active()) {
     simgrid::kernel::actor::ObjectAccessSimcallObserver observer{self, item};
     simcall(simgrid::kernel::actor::Simcall::Type::RUN_ANSWERED, code, &observer);
     item->take_ownership();
index 13e7e9a..1f3cd31 100644 (file)
@@ -82,8 +82,8 @@ SwappedContext::SwappedContext(std::function<void()>&& code, actor::ActorImpl* a
 #endif
 
       size_t size = actor->get_stacksize() + guard_size;
-#if SIMGRID_HAVE_MC
-      /* Cannot use posix_memalign when SIMGRID_HAVE_MC. Align stack by hand, and save the
+#if SIMGRID_HAVE_STATEFUL_MC
+      /* Cannot use posix_memalign when SIMGRID_HAVE_STATEFUL_MC. Align stack by hand, and save the
        * pointer returned by xbt_malloc0. */
       auto* alloc          = static_cast<unsigned char*>(xbt_malloc0(size + xbt_pagesize));
       stack_               = alloc - (reinterpret_cast<uintptr_t>(alloc) & (xbt_pagesize - 1)) + xbt_pagesize;
@@ -146,7 +146,7 @@ SwappedContext::~SwappedContext()
       XBT_WARN("Failed to remove page protection: %s", strerror(errno));
       /* try to pursue anyway */
     }
-#if SIMGRID_HAVE_MC
+#if SIMGRID_HAVE_STATEFUL_MC
     /* Retrieve the saved pointer.  See the initialization above. */
     stack_ = reinterpret_cast<unsigned char**>(stack_)[-1];
 #endif
index 7515e93..98e372d 100644 (file)
@@ -61,7 +61,7 @@ UContext::UContext(std::function<void()>&& code, actor::ActorImpl* actor, Swappe
     memcpy(ctx_addr, &arg, sizeof arg);
     makecontext(&this->uc_, (void (*)())sysv_ctx_wrapper, 2, ctx_addr[0], ctx_addr[1]);
 
-#if SIMGRID_HAVE_MC
+#if SIMGRID_HAVE_STATEFUL_MC
     if (MC_is_active())
       simgrid::mc::AppSide::get()->declare_stack(get_stack(), stack_size, &uc_);
 #endif
index 75eb88a..cebcb96 100644 (file)
@@ -747,7 +747,7 @@ void System::remove_all_modified_cnst_set()
  * If the resource is not shared (ie in FATPIPE mode), then the load is the max (not the sum)
  * of all resource usages located on this resource.
  */
-double Constraint::get_usage() const
+double Constraint::get_load() const
 {
   double result              = 0.0;
   if (sharing_policy_ != SharingPolicy::FATPIPE) {
index bfbe7d2..25fa550 100644 (file)
@@ -220,8 +220,8 @@ public:
   /** @brief Check how a constraint is shared  */
   SharingPolicy get_sharing_policy() const { return sharing_policy_; }
 
-  /** @brief Get the usage of the constraint after the last lmm solve */
-  double get_usage() const;
+  /** @brief Get the load of the constraint after the last lmm solve */
+  double get_load() const;
 
   /** @brief Sets the concurrency limit for this constraint */
   void set_concurrency_limit(int limit)
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 c69d23a..313b3fc 100644 (file)
@@ -67,9 +67,9 @@ CpuImpl* CpuImpl::set_pstate(unsigned long pstate_index)
 {
   xbt_assert(
       pstate_index < speed_per_pstate_.size(),
-      "Invalid parameters for CPU %s (pstate %lu >= length of pstates %d). Please fix your platform file, or your "
+      "Invalid parameters for CPU %s (pstate %lu >= length of pstates %zu). Please fix your platform file, or your "
       "call to change the pstate.",
-      get_cname(), pstate_index, static_cast<int>(speed_per_pstate_.size()));
+      get_cname(), pstate_index, speed_per_pstate_.size());
 
   double new_peak_speed = speed_per_pstate_[pstate_index];
   pstate_               = pstate_index;
@@ -90,14 +90,15 @@ CpuImpl* CpuImpl::set_pstate_speed(const std::vector<double>& speed_per_state)
 
 double CpuImpl::get_pstate_peak_speed(unsigned long pstate_index) const
 {
-  xbt_assert((pstate_index <= speed_per_pstate_.size()), "Invalid parameters (pstate index out of bounds)");
-
+  xbt_assert(pstate_index < speed_per_pstate_.size(), "Invalid parameters (pstate index %lu out of bounds %zu)",
+             pstate_index, speed_per_pstate_.size());
   return speed_per_pstate_[pstate_index];
 }
 
 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 f7f269f..8b6ac8a 100644 (file)
@@ -38,10 +38,6 @@ public:
   /* setup the profile file with latency events (peak latency changes due to external load).
    * Profile must contain absolute values */
   virtual void set_latency_profile(kernel::profile::Profile* profile) = 0;
-  /** @brief Set the concurrency limit for this link */
-  virtual void set_concurrency_limit(int limit) const = 0;
-  /** @brief Get the concurrency limit of this link */
-  virtual int get_concurrency_limit() const = 0;
 };
 
 } // namespace simgrid::kernel::resource
index bf67c6a..9e2f284 100644 (file)
@@ -101,10 +101,21 @@ public:
 
   lmm::Constraint* get_constraint() const { return constraint_; }
 
+  /** @brief Set the concurrency limit for this resource */
+  virtual void set_concurrency_limit(int limit) const
+  {
+    if (limit != -1)
+      get_constraint()->reset_concurrency_maximum();
+    get_constraint()->set_concurrency_limit(limit);
+  }
+
+  /** @brief Get the concurrency limit of this resource */
+  virtual int get_concurrency_limit() const { return get_constraint()->get_concurrency_limit(); }
+
   /** @brief returns the current load due to activities (in flops per second, byte per second or similar)
    *
    * The load due to external usages modeled by profile files is ignored.*/
-  virtual double get_load() const { return constraint_->get_usage(); }
+  virtual double get_load() const { return constraint_->get_load(); }
 
   bool is_used() const override { return model_->get_maxmin_system()->constraint_used(constraint_); }
 };
index 9e50e7a..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)
@@ -133,16 +137,4 @@ void StandardLinkImpl::set_latency_profile(profile::Profile* profile)
   }
 }
 
-void StandardLinkImpl::set_concurrency_limit(int limit) const
-{
-  if (limit != -1) {
-    get_constraint()->reset_concurrency_maximum();
-  }
-  get_constraint()->set_concurrency_limit(limit);
-}
-int StandardLinkImpl::get_concurrency_limit() const
-{
-  return get_constraint()->get_concurrency_limit();
-}
-
 } // namespace simgrid::kernel::resource
index 4a10210..d573fe1 100644 (file)
@@ -70,9 +70,6 @@ public:
   /* setup the profile file with latency events (peak latency changes due to external load).
    * Profile must contain absolute values */
   void set_latency_profile(kernel::profile::Profile* profile) override;
-
-  void set_concurrency_limit(int limit) const override;
-  int get_concurrency_limit() const override;
 };
 
 } // namespace simgrid::kernel::resource
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 423ed3c..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) {
@@ -340,7 +354,6 @@ NetworkNS3Model::NetworkNS3Model(const std::string& name) : NetworkModel(name)
 
   s4u::Engine::on_platform_created_cb([]() {
     /* Create the ns3 topology based on routing strategy */
-    ns3::GlobalRouteManager::DeleteGlobalRoutes(); // just in case this callback is called twice
     ns3::GlobalRouteManager::BuildGlobalRoutingDatabase();
     ns3::GlobalRouteManager::InitializeRoutes();
   });
@@ -377,6 +390,31 @@ Action* NetworkNS3Model::communicate(s4u::Host* src, s4u::Host* dst, double size
   return new NetworkNS3Action(this, size, src, dst);
 }
 
+#if SIMGRID_HAVE_NS3_GetNextEventTime
+/* If patched, ns3 is idempotent and nice to use */
+bool NetworkNS3Model::next_occurring_event_is_idempotent()
+{
+  return true;
+}
+
+double NetworkNS3Model::next_occurring_event(double sg_time)
+{
+  if (get_started_action_set()->empty()) {
+    return -1.0;
+  }
+
+  double ns3_time = ns3::Simulator::GetNextEventTime().GetSeconds();
+  XBT_DEBUG("NS3 tells that the next occuring event is at %f (it's %f in simgrid), so NS3 returns a delta of %f.",
+            ns3_time, sg_time, ns3_time - sg_time);
+  return ns3_time - sg_time;
+}
+#else
+/* NS3 is only idempotent with the appropriate patch */
+bool NetworkNS3Model::next_occurring_event_is_idempotent()
+{
+  return false;
+}
+
 double NetworkNS3Model::next_occurring_event(double now)
 {
   double time_to_next_flow_completion = 0.0;
@@ -406,11 +444,25 @@ double NetworkNS3Model::next_occurring_event(double now)
 
   return time_to_next_flow_completion;
 }
+#endif
 
 void NetworkNS3Model::update_actions_state(double now, double delta)
 {
   static std::vector<std::string> socket_to_destroy;
 
+#if SIMGRID_HAVE_NS3_GetNextEventTime
+  /* If the ns-3 model is idempotent, it won't get updated in next_occurring_event() */
+
+  if (delta >= 0) {
+    XBT_DEBUG("DO START simulator delta: %f (current simgrid time: %f; current ns3 time: %f)", delta,
+              simgrid::kernel::EngineImpl::get_clock(), ns3::Simulator::Now().GetSeconds());
+    ns3_simulator(delta);
+  } else {
+    XBT_DEBUG("don't start simulator delta: %f (current simgrid time: %f; current ns3 time: %f)", delta,
+              simgrid::kernel::EngineImpl::get_clock(), ns3::Simulator::Now().GetSeconds());
+  }
+#endif
+
   for (const auto& [ns3_socket, sgFlow] : flow_from_sock) {
     NetworkNS3Action* action = sgFlow->action_;
     XBT_DEBUG("Processing flow %p (socket %s, action %p)", sgFlow, ns3_socket.c_str(), action);
@@ -427,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);
 
@@ -455,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);
   }
 }
 
@@ -506,7 +557,7 @@ NetworkNS3Action::NetworkNS3Action(Model* model, double totalBytes, s4u::Host* s
   if (src == dst) {
     if (static bool warned = false; not warned) {
       XBT_WARN("Sending from a host %s to itself is not supported by ns-3. Every such communication finishes "
-               "immediately upon startup.",
+               "immediately upon startup in the SimGrid+ns-3 system.",
                src->get_cname());
       warned = true;
     }
@@ -533,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);
@@ -574,22 +626,27 @@ 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
 
-void ns3_simulator(double maxSeconds)
+void ns3_simulator(double maxSeconds) // maxSecond is a delay, not an absolute time
 {
   ns3::EventId id;
-  if (maxSeconds > 0.0) // If there is a maximum amount of time to run
+  if (maxSeconds >= 0.0) // If there is a maximum amount of time to run
     id = ns3::Simulator::Schedule(ns3::Seconds(maxSeconds), &ns3::Simulator::Stop);
 
-  XBT_DEBUG("Start simulator for at most %fs (current time: %f)", maxSeconds, simgrid::kernel::EngineImpl::get_clock());
+  XBT_DEBUG("Start simulator for at most %fs (current simgrid time: %f; current ns3 time: %f)", maxSeconds,
+            simgrid::kernel::EngineImpl::get_clock(), ns3::Simulator::Now().GetSeconds());
+#if SIMGRID_HAVE_NS3_GetNextEventTime
+  xbt_assert(maxSeconds >= 0.0);
+#endif
   ns3::Simulator::Run();
-  XBT_DEBUG("Simulator stopped at %fs", ns3::Simulator::Now().GetSeconds());
+  XBT_DEBUG("ns3 simulator stopped at %fs", ns3::Simulator::Now().GetSeconds());
 
-  if (maxSeconds > 0.0)
+  if (maxSeconds >= 0.0)
     id.Cancel();
 }
 
index 9ec2c4b..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:
@@ -22,7 +22,7 @@ public:
   StandardLinkImpl* create_wifi_link(const std::string& name, const std::vector<double>& bandwidth) override;
   Action* communicate(s4u::Host* src, s4u::Host* dst, double size, double rate, bool streamed) override;
   double next_occurring_event(double now) override;
-  bool next_occurring_event_is_idempotent() override { return false; }
+  bool next_occurring_event_is_idempotent() override;
   void update_actions_state(double now, double delta) override;
 };
 
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 74a0605..34b4071 100644 (file)
@@ -4,8 +4,10 @@
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
 #include "src/kernel/resource/profile/FutureEvtSet.hpp"
+#include "src/kernel/resource/Resource.hpp"
 #include "src/kernel/resource/profile/Event.hpp"
 #include "src/kernel/resource/profile/Profile.hpp"
+#include <simgrid/s4u/Engine.hpp>
 
 namespace simgrid::kernel::profile {
 
@@ -23,6 +25,23 @@ FutureEvtSet::~FutureEvtSet()
 /** @brief Schedules an event to a future date */
 void FutureEvtSet::add_event(double date, Event* evt)
 {
+  if (heap_.empty())
+    s4u::Engine::on_platform_created_cb([this]() {
+      /* Handle the events of time = 0 right after the platform creation */
+      double next_event_date;
+      while ((next_event_date = this->next_date()) != -1.0) {
+        if (next_event_date > 0)
+          break;
+
+        double value                 = -1.0;
+        resource::Resource* resource = nullptr;
+        while (auto* event = this->pop_leq(next_event_date, &value, &resource)) {
+          if (value >= 0)
+            resource->apply_event(event, value);
+        }
+      }
+    });
+
   heap_.emplace(date, evt);
 }
 
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 79374c1..9e3d355 100644 (file)
@@ -23,7 +23,7 @@ XBT_PUBLIC void simgrid_parse_assert_netpoint(const std::string& hostname, const
 XBT_PUBLIC double simgrid_parse_get_double(const std::string& s);
 XBT_PUBLIC int simgrid_parse_get_int(const std::string& s);
 
-XBT_PUBLIC void simgrid_parse(); /* Entry-point to the parser */
+XBT_PUBLIC void simgrid_parse(bool fire_on_platform_created_callback); /* Entry-point to the parser */
 XBT_PUBLIC void parse_platform_file(const std::string& file);
 
 #endif
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 df5b5cc..340b47a 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>
@@ -31,6 +32,8 @@ XBT_LOG_NEW_DEFAULT_SUBCATEGORY(platf_parse, ker_platform, "Logging specific to
 std::string simgrid_parsed_filename;                            // Currently parsed file (for the error messages)
 static std::vector<simgrid::s4u::LinkInRoute> parsed_link_list; /* temporary store of current link list of a route */
 
+static bool fire_on_platform_created_callback;
+
 /* Helping functions */
 void simgrid_parse_assert(bool cond, const std::string& msg)
 {
@@ -257,9 +260,56 @@ 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();
+  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()
@@ -532,7 +582,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) {
@@ -947,13 +997,14 @@ void simgrid_parse_close()
 }
 
 /* Call the lexer to parse the currently opened file */
-void simgrid_parse()
+void simgrid_parse(bool fire_on_platform_created_callback_param)
 {
+  fire_on_platform_created_callback = fire_on_platform_created_callback_param;
   bool err = simgrid_parse_lex();
   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 888944a..0d64feb 100644 (file)
@@ -40,7 +40,7 @@ void parse_platform_file(const std::string& file)
   simgrid_parse_open(file);
 
   /* Do the actual parsing */
-  simgrid_parse();
+  simgrid_parse(true);
 
   simgrid_parse_close();
 }
@@ -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 a7fee78..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::unique_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 fc496ad..49622dd 100644 (file)
@@ -30,52 +30,64 @@ XBT_LOG_EXTERNAL_CATEGORY(mc_global);
 
 namespace simgrid::mc {
 
-static char master_socket_name[65] = {};
-static void cleanup_master_socket()
-{
-  if (master_socket_name[0] != '\0')
-    unlink(master_socket_name);
-  master_socket_name[0] = '\0';
-}
+static std::string master_socket_name;
 
 RemoteApp::RemoteApp(const std::vector<char*>& args, bool need_memory_introspection) : app_args_(args)
 {
   if (need_memory_introspection) {
+#if SIMGRID_HAVE_STATEFUL_MC
     checker_side_     = std::make_unique<simgrid::mc::CheckerSide>(app_args_, need_memory_introspection);
     initial_snapshot_ = std::make_shared<simgrid::mc::Snapshot>(0, page_store_, *checker_side_->get_remote_memory());
+#else
+    xbt_die("SimGrid MC was compiled without memory introspection support.");
+#endif
   } else {
-    master_socket_ = socket(AF_LOCAL, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+    master_socket_ = socket(AF_UNIX,
+#ifdef __APPLE__
+                            SOCK_STREAM, /* Mac OSX does not have AF_UNIX + SOCK_SEQPACKET, even if that's faster */
+#else
+                            SOCK_SEQPACKET,
+#endif
+                            0);
     xbt_assert(master_socket_ != -1, "Cannot create the master socket: %s", strerror(errno));
 
+    master_socket_name = "/tmp/simgrid-mc-" + std::to_string(getpid());
+    master_socket_name.resize(MC_SOCKET_NAME_LEN); // truncate socket name if it's too long
+    master_socket_name.back() = '\0';              // ensure the data are null-terminated
+#ifdef __linux__
+    master_socket_name[0] = '\0'; // abstract socket, automatically removed after close
+#else
+    unlink(master_socket_name.c_str()); // remove possible stale socket before bind
+    atexit([]() {
+      if (not master_socket_name.empty())
+        unlink(master_socket_name.c_str());
+      master_socket_name.clear();
+    });
+#endif
+
     struct sockaddr_un serv_addr = {};
-    serv_addr.sun_family         = AF_LOCAL;
-    snprintf(serv_addr.sun_path, 64, "/tmp/simgrid-mc-%d", getpid());
-    strcpy(master_socket_name, serv_addr.sun_path);
-    auto addr_size = offsetof(struct sockaddr_un, sun_path) + strlen(serv_addr.sun_path);
+    serv_addr.sun_family         = AF_UNIX;
+    master_socket_name.copy(serv_addr.sun_path, MC_SOCKET_NAME_LEN);
 
-    xbt_assert(bind(master_socket_, (struct sockaddr*)&serv_addr, addr_size) >= 0,
-               "Cannot bind the master socket to %s: %s.", serv_addr.sun_path, strerror(errno));
-    atexit(cleanup_master_socket);
+    xbt_assert(bind(master_socket_, (struct sockaddr*)&serv_addr, sizeof serv_addr) >= 0,
+               "Cannot bind the master socket to %c%s: %s.", (serv_addr.sun_path[0] ? serv_addr.sun_path[0] : '@'),
+               serv_addr.sun_path + 1, strerror(errno));
 
     xbt_assert(listen(master_socket_, SOMAXCONN) >= 0, "Cannot listen to the master socket: %s.", strerror(errno));
 
     application_factory_ = std::make_unique<simgrid::mc::CheckerSide>(app_args_, need_memory_introspection);
-    checker_side_        = application_factory_->clone(master_socket_);
+    checker_side_        = application_factory_->clone(master_socket_, master_socket_name);
   }
 }
 
-RemoteApp::~RemoteApp()
-{
-  initial_snapshot_ = nullptr;
-  checker_side_     = nullptr;
-}
-
 void RemoteApp::restore_initial_state()
 {
-  if (initial_snapshot_ == nullptr) { // No memory introspection
-    checker_side_ = application_factory_->clone(master_socket_);
-  } else
+  if (initial_snapshot_ == nullptr) // No memory introspection
+    checker_side_ = application_factory_->clone(master_socket_, master_socket_name);
+#if SIMGRID_HAVE_STATEFUL_MC
+  else
     initial_snapshot_->restore(*checker_side_->get_remote_memory());
+#endif
 }
 
 unsigned long RemoteApp::get_maxpid() const
@@ -104,6 +116,10 @@ void RemoteApp::get_actors_status(std::map<aid_t, ActorState>& whereto) const
   //                    <----- send ACTORS_STATUS_REPLY_COUNT
   //                    <----- send `N` ACTORS_STATUS_REPLY_TRANSITION (s_mc_message_actors_status_one_t)
   //                    <----- send `M` ACTORS_STATUS_REPLY_SIMCALL (s_mc_message_simcall_probe_one_t)
+  //
+  // Note that we also receive disabled transitions, because the guiding strategies need them to decide what could
+  // unlock actors.
+
   checker_side_->get_channel().send(MessageType::ACTORS_STATUS);
 
   s_mc_message_actors_status_answer_t answer;
@@ -111,7 +127,7 @@ void RemoteApp::get_actors_status(std::map<aid_t, ActorState>& whereto) const
   xbt_assert(answer_size != -1, "Could not receive message");
   xbt_assert(answer_size == sizeof answer, "Broken message (size=%zd; expected %zu)", answer_size, sizeof answer);
   xbt_assert(answer.type == MessageType::ACTORS_STATUS_REPLY_COUNT,
-             "Received unexpected message %s (%i); expected MessageType::ACTORS_STATUS_REPLY_COUNT (%i)",
+             "%d Received unexpected message %s (%i); expected MessageType::ACTORS_STATUS_REPLY_COUNT (%i)", getpid(),
              to_c_str(answer.type), (int)answer.type, (int)MessageType::ACTORS_STATUS_REPLY_COUNT);
 
   // Message sanity checks
@@ -129,7 +145,7 @@ void RemoteApp::get_actors_status(std::map<aid_t, ActorState>& whereto) const
 
   for (const auto& actor : status) {
     std::vector<std::shared_ptr<Transition>> actor_transitions;
-    int n_transitions = actor.enabled ? actor.max_considered : 0;
+    int n_transitions = actor.max_considered;
     for (int times_considered = 0; times_considered < n_transitions; times_considered++) {
       s_mc_message_simcall_probe_one_t probe;
       ssize_t received = checker_side_->get_channel().receive(probe);
@@ -167,7 +183,7 @@ void RemoteApp::check_deadlock() const
              "--cfg=model-check/replay:'%s'",
              explo->get_record_trace().to_string().c_str());
     explo->log_state();
-    throw DeadlockError();
+    throw McError(ExitStatus::DEADLOCK);
   }
 }
 
@@ -184,8 +200,10 @@ Transition* RemoteApp::handle_simcall(aid_t aid, int times_considered, bool new_
   m.times_considered_              = times_considered;
   checker_side_->get_channel().send(m);
 
+#if SIMGRID_HAVE_STATEFUL_MC
   if (auto* memory = get_remote_process_memory(); memory != nullptr)
     memory->clear_cache();
+#endif
   if (checker_side_->running())
     checker_side_->dispatch_events(); // The app may send messages while processing the transition
 
index 693a220..dbb5e5d 100644 (file)
@@ -26,9 +26,13 @@ namespace simgrid::mc {
  */
 class XBT_PUBLIC RemoteApp {
 private:
-  std::unique_ptr<CheckerSide> checker_side_;
+#if SIMGRID_HAVE_STATEFUL_MC
   PageStore page_store_{500};
   std::shared_ptr<simgrid::mc::Snapshot> initial_snapshot_;
+#else
+  void* initial_snapshot_ = nullptr; // The code tests it to decide whether to use the refork exec path
+#endif
+  std::unique_ptr<CheckerSide> checker_side_;
   std::unique_ptr<CheckerSide> application_factory_; // when no meminfo, create checker_side_ by cloning this one
   int master_socket_ = -1;
 
@@ -48,12 +52,10 @@ public:
    */
   explicit RemoteApp(const std::vector<char*>& args, bool need_memory_introspection);
 
-  ~RemoteApp();
-
   void restore_initial_state();
   void wait_for_requests();
 
-  /** Ask to the application to check for a deadlock. If so, do an error message and throw a DeadlockError. */
+  /** Ask to the application to check for a deadlock. If so, do an error message and throw a McError(DEADLOCK). */
   void check_deadlock() const;
 
   /** Ask the application to run post-mortem analysis, and maybe to stop ASAP */
@@ -68,10 +70,12 @@ public:
   /** Take a transition. A new Transition is created iff the last parameter is true */
   Transition* handle_simcall(aid_t aid, int times_considered, bool new_transition);
 
+#if SIMGRID_HAVE_STATEFUL_MC
   /* Get the memory of the remote process */
   RemoteProcessMemory* get_remote_process_memory() { return checker_side_->get_remote_memory(); }
 
   PageStore& get_page_store() { return page_store_; }
+#endif
 };
 } // namespace simgrid::mc
 
index 9a637e7..007b179 100644 (file)
@@ -5,10 +5,13 @@
 
 #include "src/mc/api/State.hpp"
 #include "src/mc/api/strategy/BasicStrategy.hpp"
-#include "src/mc/api/strategy/WaitStrategy.hpp"
+#include "src/mc/api/strategy/MaxMatchComm.hpp"
+#include "src/mc/api/strategy/MinMatchComm.hpp"
+#include "src/mc/api/strategy/UniformStrategy.hpp"
 #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");
@@ -20,43 +23,53 @@ long State::expended_states_ = 0;
 State::State(RemoteApp& remote_app) : num_(++expended_states_)
 {
   XBT_VERB("Creating a guide for the state");
+
+  srand(_sg_mc_random_seed);
+  
   if (_sg_mc_strategy == "none")
     strategy_ = std::make_shared<BasicStrategy>();
-  if (_sg_mc_strategy == "nb_wait")
-    strategy_ = std::make_shared<WaitStrategy>();
-
-  recipe_ = std::list<Transition*>();
+  else if (_sg_mc_strategy == "max_match_comm")
+    strategy_ = std::make_shared<MaxMatchComm>();
+  else if (_sg_mc_strategy == "min_match_comm")
+    strategy_ = std::make_shared<MinMatchComm>();
+  else if (_sg_mc_strategy == "uniform") 
+    strategy_ = std::make_shared<UniformStrategy>();
+  else
+    THROW_IMPOSSIBLE;
 
   remote_app.get_actors_status(strategy_->actors_to_run_);
 
+#if SIMGRID_HAVE_STATEFUL_MC
   /* Stateful model checking */
   if ((_sg_mc_checkpoint > 0 && (num_ % _sg_mc_checkpoint == 0)) || _sg_mc_termination)
     system_state_ = std::make_shared<simgrid::mc::Snapshot>(num_, remote_app.get_page_store(),
                                                             *remote_app.get_remote_process_memory());
+#endif
 }
 
 State::State(RemoteApp& remote_app, std::shared_ptr<State> parent_state)
-    : num_(++expended_states_), parent_state_(parent_state)
+    : incoming_transition_(parent_state->get_transition_out()), num_(++expended_states_), parent_state_(parent_state)
 {
-
-  if (_sg_mc_strategy == "none")
+    
+   if (_sg_mc_strategy == "none")
     strategy_ = std::make_shared<BasicStrategy>();
-  if (_sg_mc_strategy == "nb_wait")
-    strategy_ = std::make_shared<WaitStrategy>();
-  *strategy_ = *(parent_state->strategy_);
-
-  recipe_ = std::list(parent_state_->get_recipe());
-  recipe_.push_back(parent_state_->get_transition());
+  else if (_sg_mc_strategy == "max_match_comm")
+    strategy_ = std::make_shared<MaxMatchComm>();
+  else if (_sg_mc_strategy == "min_match_comm")
+    strategy_ = std::make_shared<MinMatchComm>();
+  else if (_sg_mc_strategy == "uniform") 
+    strategy_ = std::make_shared<UniformStrategy>();
+  else
+    THROW_IMPOSSIBLE;
+  strategy_->copy_from(parent_state_->strategy_.get());
 
   remote_app.get_actors_status(strategy_->actors_to_run_);
 
-  for (auto const& [aid, _] : strategy_->actors_to_run_)
-    strategy_->penalties_[aid] = parent_state_->strategy_->penalties_[aid];
-
-  /* Stateful model checking */
+#if SIMGRID_HAVE_STATEFUL_MC /* Stateful model checking */
   if ((_sg_mc_checkpoint > 0 && (num_ % _sg_mc_checkpoint == 0)) || _sg_mc_termination)
     system_state_ = std::make_shared<simgrid::mc::Snapshot>(num_, remote_app.get_page_store(),
                                                             *remote_app.get_remote_process_memory());
+#endif
 
   /* If we want sleep set reduction, copy the sleep set and eventually removes things from it */
   if (_sg_mc_sleep_set) {
@@ -64,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 parent_state_->get_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 >>%s<<",
-                  transition.to_string().c_str(), parent_state_->get_transition()->to_string().c_str());
+        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());
     }
   }
 }
@@ -86,18 +98,13 @@ std::size_t State::count_todo() const
 std::size_t State::count_todo_multiples() const
 {
   size_t count = 0;
-  for (auto& [_, actor] : strategy_->actors_to_run_)
+  for (auto const& [_, actor] : strategy_->actors_to_run_)
     if (actor.is_todo())
       count += actor.get_times_not_considered();
 
   return count;
 }
 
-Transition* State::get_transition() const
-{
-  return transition_;
-}
-
 aid_t State::next_transition() const
 {
   XBT_DEBUG("Search for an actor to run. %zu actors to consider", strategy_->actors_to_run_.size());
@@ -121,32 +128,18 @@ aid_t State::next_transition() const
   return -1;
 }
 
-std::pair<aid_t, double> State::next_transition_guided() const
+std::pair<aid_t, int> State::next_transition_guided() const
 {
-
-  for (auto const& [aid, penalty] : strategy_->penalties_)
-    XBT_DEBUG("Current penalty for actor %ld:%f", aid, strategy_->penalties_[aid]);
-
-  for (auto const& [aid, actor] : strategy_->actors_to_run_) {
-
-    /* Only consider actors (1) marked as interleaving by the checker and (2) currently enabled in the application */
-    if (not actor.is_todo() || not actor.is_enabled() || actor.is_done()) {
-
-      if (not actor.is_todo())
-        XBT_DEBUG("Can't run actor %ld because it is not todo", aid);
-
-      if (not actor.is_enabled())
-        XBT_DEBUG("Can't run actor %ld because it is not enabled", aid);
-
-      if (actor.is_done())
-        XBT_DEBUG("Can't run actor %ld because it has already been done", aid);
-    }
-  }
   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
-void State::execute_next(aid_t next, RemoteApp& app)
+std::shared_ptr<Transition> State::execute_next(aid_t next, RemoteApp& app)
 {
   // First, warn the guide, so it knows how to build a proper child state
   strategy_->execute_next(next, app);
@@ -157,7 +150,7 @@ void 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);
 
@@ -179,11 +172,129 @@ void State::execute_next(aid_t next, RemoteApp& app)
   //  2. what action actor `next` was able to take given `times_considered`
   // The latter update is important as *more* information is potentially available
   // about a transition AFTER it has executed.
-  transition_ = just_executed;
-
-  auto executed_transition = std::unique_ptr<Transition>(just_executed);
-  actor_state.set_transition(std::move(executed_transition), times_considered);
+  outgoing_transition_ = std::shared_ptr<Transition>(just_executed);
 
+  actor_state.set_transition(outgoing_transition_, times_considered);
   app.wait_for_requests();
+
+  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 f67d703..cfa440d 100644 (file)
@@ -7,34 +7,27 @@
 #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/sosp/Snapshot.hpp"
+#include "src/mc/explo/odpor/WakeupTree.hpp"
 #include "src/mc/transition/Transition.hpp"
 
+#if SIMGRID_HAVE_STATEFUL_MC
+#include "src/mc/sosp/Snapshot.hpp"
+#endif
+
 namespace simgrid::mc {
 
 /* A node in the exploration graph (kind-of) */
 class XBT_PRIVATE State : public xbt::Extendable<State> {
   static long expended_states_; /* Count total amount of states, for stats */
 
-  /**
-   * @brief An empty transition that leads to this state by default
-   */
-  const std::unique_ptr<Transition> default_transition_ = std::make_unique<Transition>();
-
-  /**
-   * @brief The outgoing transition: what was the last transition that
-   * we took to leave this state?
-   *
-   * The owner of the transition is the `ActorState` instance which exists in this state,
-   * or a reference to the internal default transition `Transition()` if no transition has been
-   * set
-   */
-  Transition* transition_ = default_transition_.get();
+  /** @brief The outgoing transition is the last transition that we took to leave this state.  */
+  std::shared_ptr<Transition> outgoing_transition_ = nullptr;
 
-  /** @brief A list of transition to be replayed in order to get in this state. */
-  std::list<Transition*> recipe_;
+  /** @brief The incoming transition is what led to this state, coming from its parent  */
+  std::shared_ptr<Transition> incoming_transition_ = nullptr;
 
   /** Sequential state ID (used for debugging) */
   long num_ = 0;
@@ -51,23 +44,37 @@ 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_;
 
-public:
-  bool is_opened = false;
+  /**
+   * 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);
   explicit State(RemoteApp& remote_app, std::shared_ptr<State> parent_state);
   /* 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 a double 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, double> next_transition_guided() const;
+  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;
 
-  /* Explore a new path on the remote app; the parameter 'next' must be the result of a previous call to
-   * next_transition() */
-  void execute_next(aid_t next, RemoteApp& app);
+  /**
+   * @brief Explore a new path on the remote app; the parameter 'next' must be the result of a previous call to
+   * next_transition()
+   */
+  std::shared_ptr<Transition> execute_next(aid_t next, RemoteApp& app);
 
   long get_num() const { return num_; }
   std::size_t count_todo() const;
@@ -75,32 +82,95 @@ 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 */
-  void consider_one(aid_t aid) { strategy_->consider_one(aid); }
-  void consider_best() { strategy_->consider_best(); }
-  unsigned long consider_all() { return strategy_->consider_all(); }
+   *  + 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(); }
 
   bool is_actor_done(aid_t actor) const { return strategy_->actors_to_run_.at(actor).is_done(); }
-  Transition* get_transition() const;
-  void set_transition(Transition* t) { transition_ = t; }
-  std::shared_ptr<State> get_parent_state() { return parent_state_; }
-  std::list<Transition*> get_recipe() const { return recipe_; }
+  std::shared_ptr<Transition> get_transition_out() const { return outgoing_transition_; }
+  std::shared_ptr<Transition> get_transition_in() const { return incoming_transition_; }
+  std::shared_ptr<State> get_parent_state() const { return parent_state_; }
 
   std::map<aid_t, ActorState> const& get_actors_list() const { return strategy_->actors_to_run_; }
 
   unsigned long get_actor_count() const { return strategy_->actors_to_run_.size(); }
-  bool is_actor_enabled(aid_t actor) { return strategy_->actors_to_run_.at(actor).is_enabled(); }
+  bool is_actor_enabled(aid_t actor) const { return strategy_->actors_to_run_.at(actor).is_enabled(); }
 
   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(const 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 276d1ff..42676d4 100644 (file)
@@ -6,83 +6,50 @@
 #ifndef SIMGRID_MC_BASICSTRATEGY_HPP
 #define SIMGRID_MC_BASICSTRATEGY_HPP
 
-#include "src/mc/transition/Transition.hpp"
-#include "src/mc/transition/TransitionAny.hpp"
-#include "src/mc/transition/TransitionComm.hpp"
+#include "Strategy.hpp"
 
 namespace simgrid::mc {
 
-/** Basic MC guiding class which corresponds to no guide at all (random choice) */
+/** Basic MC guiding class which corresponds to no guide. When asked for different states
+ *  it will follow a depth first search politics to minize the number of opened states. */
 class BasicStrategy : public Strategy {
+    int depth_ = 100000; // Arbitrary starting point. next_transition must return a positiv value to work with threshold in DFSExplorer
+
 public:
-  void operator=(const BasicStrategy& other) { this->penalties_ = other.penalties_; }
+  void copy_from(const Strategy* strategy) override
+  {
+    const BasicStrategy* cast_strategy = static_cast<BasicStrategy const*>(strategy);
+    xbt_assert(cast_strategy != nullptr);
+    depth_ = cast_strategy->depth_ - 1;
+    xbt_assert(depth_ > 0, "The exploration reached a depth greater than 100000. We will stop here to prevent weird interaction with DFSExplorer.");
+  }
+  BasicStrategy()                     = default;
+  ~BasicStrategy() override           = default;
 
-  std::pair<aid_t, double> best_transition(bool need_to_be_todo) const
+  std::pair<aid_t, int> next_transition() const override
   {
-    auto min = std::make_pair(-1, 10000);
     for (auto const& [aid, actor] : actors_to_run_) {
       /* Only consider actors (1) marked as interleaving by the checker and (2) currently enabled in the application */
-      if ((not actor.is_todo() and need_to_be_todo) || not actor.is_enabled() || actor.is_done()) {
+      if (not actor.is_todo() || not actor.is_enabled() || actor.is_done()) {
         continue;
       }
-      if (actor.get_transition(actor.get_times_considered())->type_ != Transition::Type::TESTANY)
-        return std::make_pair(aid, 0.0);
-
-      double dist;
-
-      TestAnyTransition* transition =
-          static_cast<TestAnyTransition*>(actor.get_transition(actor.get_times_considered()));
 
-      auto iterator = penalties_.find(aid);
-      if (iterator != penalties_.end())
-        dist = (*iterator).second;
-      else
-        dist = 0;
-
-      if (not transition->result())
-        dist += 5000;
-
-      if (dist < min.second)
-        min = std::make_pair(aid, dist);
+      return std::make_pair(aid, depth_);
     }
-
-    if (min.first == -1)
-      return std::make_pair(-1, -1000);
-    return min;
-  }
-
-  std::pair<aid_t, double> next_transition() const override { return best_transition(true); }
-
-  void execute_next(aid_t aid, RemoteApp& app) override
-  {
-    auto actor = actors_to_run_.at(aid);
-    if (actor.get_transition(actor.get_times_considered())->type_ == Transition::Type::TESTANY) {
-      TestAnyTransition* transition =
-          static_cast<TestAnyTransition*>(actor.get_transition(actor.get_times_considered()));
-      if (not transition->result()) {
-        penalties_[aid] = penalties_[aid] + 1.0;
-        return;
-      }
-    }
-    penalties_[aid] = 0;
+    return std::make_pair(-1, depth_);
   }
 
   void consider_best() override
   {
-    const auto& [aid, _] = this->best_transition(false);
-    auto actor           = actors_to_run_.find(aid);
-    if (actor != actors_to_run_.end()) {
-      actor->second.mark_todo();
-      return;
-    }
-    for (auto& [_, actor] : actors_to_run_) {
-      if (actor.is_todo())
-        return;
-      if (actor.is_enabled() and not actor.is_done()) {
-        actor.mark_todo();
-        return;
-      }
-    }
+    for (auto& [_, actor] : actors_to_run_) 
+       if (actor.is_todo())
+           return;
+
+    for (auto& [_, actor] : actors_to_run_) 
+       if (actor.is_enabled() and not actor.is_done()) 
+           actor.mark_todo();
+      
+    
   }
 };
 
diff --git a/src/mc/api/strategy/MaxMatchComm.hpp b/src/mc/api/strategy/MaxMatchComm.hpp
new file mode 100644 (file)
index 0000000..b5f974c
--- /dev/null
@@ -0,0 +1,120 @@
+/* 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_WAITSTRATEGY_HPP
+#define SIMGRID_MC_WAITSTRATEGY_HPP
+
+#include "src/mc/transition/TransitionComm.hpp"
+
+namespace simgrid::mc {
+
+/** Wait MC guiding class that aims at minimizing the number of in-fly communication.
+ *  When possible, it will try to match corresponding in-fly communications. */
+class MaxMatchComm : public Strategy {
+
+  /** Stores for each mailbox what kind of transition is waiting on it.
+   *  Negative number means that much recv are waiting on that mailbox, while
+   *  a positiv number means that much send are waiting there. */
+  std::map<unsigned, int> mailbox_;
+    int value_of_state_ = 0; // used to valuate the state. Corresponds to the number of in-fly communications
+    // The two next values are used to save the operation we execute so the next strategy can update its field accordingly
+  Transition::Type last_transition_; 
+  unsigned last_mailbox_ = 0;
+
+public:
+  void copy_from(const Strategy* strategy) override
+  {
+    const MaxMatchComm* cast_strategy = static_cast<MaxMatchComm const*>(strategy);
+    xbt_assert(cast_strategy != nullptr);
+    for (auto& [id, val] : cast_strategy->mailbox_)
+      mailbox_[id] = val;
+    if (cast_strategy->last_transition_ == Transition::Type::COMM_ASYNC_RECV)
+      mailbox_[cast_strategy->last_mailbox_]--;
+    if (cast_strategy->last_transition_ == Transition::Type::COMM_ASYNC_SEND)
+      mailbox_[cast_strategy->last_mailbox_]++;
+
+    for (auto const& [_, val] : mailbox_)
+       value_of_state_ += std::abs(val);
+  }
+  MaxMatchComm()                     = default;
+  ~MaxMatchComm() override           = default;
+
+  std::pair<aid_t, int> next_transition() const override
+  {
+    std::pair<aid_t, int> if_no_match = std::make_pair(-1, 0);
+    for (auto const& [aid, actor] : actors_to_run_) {
+      if (not actor.is_todo() || not actor.is_enabled() || actor.is_done())
+        continue;
+
+      const Transition* transition = actor.get_transition(actor.get_times_considered()).get();
+
+      const CommRecvTransition* cast_recv = static_cast<CommRecvTransition const*>(transition);
+      if (cast_recv != nullptr and mailbox_.count(cast_recv->get_mailbox()) > 0 and
+          mailbox_.at(cast_recv->get_mailbox()) > 0)
+        return std::make_pair(aid, value_of_state_ - 1); // This means we have waiting send corresponding to this recv
+
+      const CommSendTransition* cast_send = static_cast<CommSendTransition const*>(transition);
+      if (cast_send != nullptr and mailbox_.count(cast_send->get_mailbox()) > 0 and
+          mailbox_.at(cast_send->get_mailbox()) < 0)
+        return std::make_pair(aid, value_of_state_ - 1); // This means we have waiting recv corresponding to this send
+
+      if (if_no_match.first == -1)
+        if_no_match = std::make_pair(aid, value_of_state_);
+    }
+    return if_no_match;
+  }
+
+  void execute_next(aid_t aid, RemoteApp& app) override
+  {
+    const Transition* transition = actors_to_run_.at(aid).get_transition(actors_to_run_.at(aid).get_times_considered()).get();
+    last_transition_             = transition->type_;
+
+    const CommRecvTransition* cast_recv = static_cast<CommRecvTransition const*>(transition);
+    if (cast_recv != nullptr)
+      last_mailbox_ = cast_recv->get_mailbox();
+
+    const CommSendTransition* cast_send = static_cast<CommSendTransition const*>(transition);
+    if (cast_send != nullptr)
+      last_mailbox_ = cast_send->get_mailbox();
+  }
+
+  void consider_best() override
+  {
+    for (auto& [aid, actor] : actors_to_run_)
+      if (actor.is_todo())
+        return;
+
+    for (auto& [aid, actor] : actors_to_run_) {
+      if (not actor.is_enabled() || actor.is_done())
+        continue;
+
+      const Transition* transition = actor.get_transition(actor.get_times_considered()).get();
+
+      const CommRecvTransition* cast_recv = static_cast<CommRecvTransition const*>(transition);
+      if (cast_recv != nullptr and mailbox_.count(cast_recv->get_mailbox()) > 0 and
+          mailbox_.at(cast_recv->get_mailbox()) > 0) {
+        actor.mark_todo();
+        return;
+      }
+
+      const CommSendTransition* cast_send = static_cast<CommSendTransition const*>(transition);
+      if (cast_send != nullptr and mailbox_.count(cast_send->get_mailbox()) > 0 and
+          mailbox_.at(cast_send->get_mailbox()) < 0) {
+        actor.mark_todo();
+        return;
+      }
+    }
+    for (auto& [_, actor] : actors_to_run_) {
+      if (actor.is_enabled() and not actor.is_done()) {
+        actor.mark_todo();
+        return;
+      }
+    }
+  }
+};
+
+} // namespace simgrid::mc
+
+#endif
diff --git a/src/mc/api/strategy/MinMatchComm.hpp b/src/mc/api/strategy/MinMatchComm.hpp
new file mode 100644 (file)
index 0000000..50fe692
--- /dev/null
@@ -0,0 +1,123 @@
+/* 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_MINWAITTAKEN_HPP
+#define SIMGRID_MC_MINWAITTAKEN_HPP
+
+#include "src/mc/transition/TransitionComm.hpp"
+
+namespace simgrid::mc {
+
+/** Wait MC guiding class that aims at maximizing the number of in-fly communication.
+ *  When possible, it will try not to match communications. */
+class MinMatchComm : public Strategy {
+
+    
+  /** Stores for each mailbox what kind of transition is waiting on it.
+   *  Negative number means that much recv are waiting on that mailbox, while
+   *  a positiv number means that much send are waiting there. */
+  std::map<unsigned, int> mailbox_;
+  int value_of_state_ = 100000; // used to valuate the state. Corresponds to the number of in-fly communications
+    // The two next values are used to save the operation we execute so the next strategy can update its field accordingly
+  Transition::Type last_transition_;
+  unsigned last_mailbox_ = 0;
+
+public:
+  void copy_from(const Strategy* strategy) override
+  {
+      const MinMatchComm* cast_strategy = static_cast<MinMatchComm const*>(strategy);
+      xbt_assert(cast_strategy != nullptr);
+      for (auto& [id, val] : cast_strategy->mailbox_)
+         mailbox_[id] = val;
+      if (cast_strategy->last_transition_ == Transition::Type::COMM_ASYNC_RECV)
+         mailbox_[cast_strategy->last_mailbox_]--;
+      if (cast_strategy->last_transition_ == Transition::Type::COMM_ASYNC_SEND)
+         mailbox_[cast_strategy->last_mailbox_]++;
+
+      for (auto const& [_, val] : mailbox_) 
+         value_of_state_ -= std::abs(val);
+      if (value_of_state_ < 0)
+         value_of_state_ = 0;
+  }
+    MinMatchComm()                     = default;
+  ~MinMatchComm() override           = default;
+
+  std::pair<aid_t, int> next_transition() const override
+  {
+    std::pair<aid_t, int> if_no_match = std::make_pair(-1, 0);
+    for (auto const& [aid, actor] : actors_to_run_) {
+      if (not actor.is_todo() || not actor.is_enabled() || actor.is_done())
+        continue;
+
+      const Transition* transition = actor.get_transition(actor.get_times_considered()).get();
+
+      const CommRecvTransition* cast_recv = static_cast<CommRecvTransition const*>(transition);
+      if (cast_recv != nullptr and mailbox_.count(cast_recv->get_mailbox()) > 0 and
+          mailbox_.at(cast_recv->get_mailbox()) <= 0)
+        return std::make_pair(aid, value_of_state_ - 1); // This means we don't have waiting recv corresponding to this recv
+
+      const CommSendTransition* cast_send = static_cast<CommSendTransition const*>(transition);
+      if (cast_send != nullptr and mailbox_.count(cast_send->get_mailbox()) > 0 and
+          mailbox_.at(cast_send->get_mailbox()) >= 0)
+        return std::make_pair(aid, value_of_state_ - 1); // This means we don't have waiting recv corresponding to this send
+
+      if (if_no_match.first == -1)
+        if_no_match = std::make_pair(aid, value_of_state_);
+    }
+    return if_no_match;
+  }
+
+  void execute_next(aid_t aid, RemoteApp& app) override
+  {
+      const Transition* transition = actors_to_run_.at(aid).get_transition(actors_to_run_.at(aid).get_times_considered()).get();
+    last_transition_             = transition->type_;
+
+    const CommRecvTransition* cast_recv = static_cast<CommRecvTransition const*>(transition);
+    if (cast_recv != nullptr)
+      last_mailbox_ = cast_recv->get_mailbox();
+
+    const CommSendTransition* cast_send = static_cast<CommSendTransition const*>(transition);
+    if (cast_send != nullptr)
+      last_mailbox_ = cast_send->get_mailbox();
+  }
+
+  void consider_best() override
+  {
+      for (auto& [aid, actor] : actors_to_run_)
+      if (actor.is_todo())
+        return;
+
+    for (auto& [aid, actor] : actors_to_run_) {
+      if (not actor.is_enabled() || actor.is_done())
+        continue;
+
+      const Transition* transition = actor.get_transition(actor.get_times_considered()).get();
+
+      const CommRecvTransition* cast_recv = static_cast<CommRecvTransition const*>(transition);
+      if (cast_recv != nullptr and mailbox_.count(cast_recv->get_mailbox()) > 0 and
+          mailbox_.at(cast_recv->get_mailbox()) <= 0) {
+        actor.mark_todo();
+        return;
+      }
+
+      const CommSendTransition* cast_send = static_cast<CommSendTransition const*>(transition);
+      if (cast_send != nullptr and mailbox_.count(cast_send->get_mailbox()) > 0 and
+          mailbox_.at(cast_send->get_mailbox()) >= 0) {
+        actor.mark_todo();
+        return;
+      }
+    }
+    for (auto& [_, actor] : actors_to_run_) {
+      if (actor.is_enabled() and not actor.is_done()) {
+        actor.mark_todo();
+        return;
+      }
+    }
+  }
+};
+
+} // namespace simgrid::mc
+
+#endif
index 92615d5..8af7774 100644 (file)
@@ -3,41 +3,44 @@
 /* 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 {
 protected:
-  /** State's exploration status by actor. Not all the actors are there, only the ones that are ready-to-run in this
-   * state */
+  /** State's exploration status by actor. All actors should be present, eventually disabled for now. */
   std::map<aid_t, ActorState> actors_to_run_;
 
 public:
-  /** Used to store penalties computed by the strategy to each actor */
-  std::map<aid_t, double> penalties_;
-  virtual ~Strategy()                                      = default;
-  void operator=(const Strategy&)
-  { /* nothing to copy over while cloning */
-    return;
-  }
+  virtual void copy_from(const Strategy*) = 0;
+  Strategy()                              = default;
+  virtual ~Strategy()                     = default;
 
-  virtual std::pair<aid_t, double> next_transition() const = 0;
-  virtual void execute_next(aid_t aid, RemoteApp& app)     = 0;
-  virtual void consider_best()                             = 0;
+  virtual std::pair<aid_t, int> next_transition() const = 0;
+  virtual void execute_next(aid_t aid, RemoteApp& app)  = 0;
 
   // Mark the first enabled and not yet done transition as todo
   // If there's already a transition marked as todo, does nothing
+  virtual void consider_best() = 0;
+
+  // Mark aid as todo. If it makes no sense, ie. if it is already done or not enabled,
+  // raise an error
   void consider_one(aid_t aid)
   {
     xbt_assert(actors_to_run_.at(aid).is_enabled() and not actors_to_run_.at(aid).is_done(),
                "Tried to mark as TODO actor %ld but it is either not enabled or already done", aid);
     actors_to_run_.at(aid).mark_todo();
   }
-  // Matk as todo all actors enabled that are not done yet
+  // Mark as todo all actors enabled that are not done yet and return the number
+  // of marked actors
   unsigned long consider_all()
   {
     unsigned long count = 0;
diff --git a/src/mc/api/strategy/UniformStrategy.hpp b/src/mc/api/strategy/UniformStrategy.hpp
new file mode 100644 (file)
index 0000000..72f1a37
--- /dev/null
@@ -0,0 +1,100 @@
+/* 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_UNIFORMSTRATEGY_HPP
+#define SIMGRID_MC_UNIFORMSTRATEGY_HPP
+
+#include "src/mc/transition/Transition.hpp"
+
+namespace simgrid::mc {
+
+/** Guiding strategy that valuate states randomly */
+class UniformStrategy : public Strategy {
+  std::map<aid_t, int> valuation;
+
+public:
+  UniformStrategy()
+  {
+    for (long aid = 0; aid < 10; aid++)
+      valuation[aid] = rand() % 1000;
+  }
+  void copy_from(const Strategy* strategy) override
+  {
+    for (auto& [aid, _] : actors_to_run_)
+      valuation[aid] = rand() % 1000;
+  }
+
+  std::pair<aid_t, int> next_transition() const override
+  {
+    int possibilities = 0;
+
+    // Consider only valid actors
+    for (auto const& [aid, actor] : actors_to_run_) {
+      if (actor.is_todo() and (not actor.is_done()) and actor.is_enabled())
+        possibilities++;
+    }
+
+    int chosen;
+    if (possibilities == 0)
+      return std::make_pair(-1, 100000);
+    if (possibilities == 1)
+      chosen = 0;
+    else
+      chosen = rand() % possibilities;
+
+    for (auto const& [aid, actor] : actors_to_run_) {
+      if ((not actor.is_todo()) or actor.is_done() or (not actor.is_enabled()))
+        continue;
+      if (chosen == 0) {
+        return std::make_pair(aid, valuation.at(aid));
+      }
+      chosen--;
+    }
+
+    return std::make_pair(-1, 100000);
+  }
+
+  void execute_next(aid_t aid, RemoteApp& app) override {}
+
+  void consider_best() override
+  {
+
+    int possibilities = 0;
+    // Consider only valid actors
+    // If some actor are already considered as todo, skip
+    for (auto const& [aid, actor] : actors_to_run_) {
+      if (valuation.count(aid) == 0)
+        for (auto& [aid, _] : actors_to_run_)
+          valuation[aid] = rand() % 1000;
+      if (actor.is_todo())
+        return;
+      if (actor.is_enabled() and not actor.is_done())
+        possibilities++;
+    }
+
+    int chosen;
+    if (possibilities == 0)
+      return;
+    if (possibilities == 1)
+      chosen = 0;
+    else
+      chosen = rand() % possibilities;
+
+    for (auto& [aid, actor] : actors_to_run_) {
+      if (not actor.is_enabled() or actor.is_done())
+        continue;
+      if (chosen == 0) {
+        actor.mark_todo();
+        return;
+      }
+      chosen--;
+    }
+    THROW_IMPOSSIBLE; // One actor should be marked as todo before
+  }
+};
+
+} // namespace simgrid::mc
+
+#endif
diff --git a/src/mc/api/strategy/WaitStrategy.hpp b/src/mc/api/strategy/WaitStrategy.hpp
deleted file mode 100644 (file)
index edf75b2..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/* 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_WAITSTRATEGY_HPP
-#define SIMGRID_MC_WAITSTRATEGY_HPP
-
-#include "src/mc/transition/Transition.hpp"
-
-namespace simgrid::mc {
-
-/** Wait MC guiding class that aims at minimizing the number of in-fly communication.
- *  When possible, it will try to take the wait transition. */
-class WaitStrategy : public Strategy {
-  double taken_wait_ = 0;
-  bool taking_wait_  = false;
-
-public:
-  void operator=(const WaitStrategy& guide) { taken_wait_ = guide.taken_wait_; }
-
-  bool is_transition_wait(Transition::Type type) const
-  {
-    return type == Transition::Type::WAITANY or type == Transition::Type::BARRIER_WAIT or
-           type == Transition::Type::MUTEX_WAIT or type == Transition::Type::SEM_WAIT;
-  }
-
-  std::pair<aid_t, double> next_transition() const override
-  {
-    std::pair<aid_t, double> if_no_wait = std::make_pair(-1, 0.0);
-    for (auto const& [aid, actor] : actors_to_run_) {
-      if (not actor.is_todo() || not actor.is_enabled() || actor.is_done())
-        continue;
-      if (is_transition_wait(actor.get_transition(actor.get_times_considered())->type_))
-        return std::make_pair(aid, -(taken_wait_ + 1));
-      if_no_wait = std::make_pair(aid, -taken_wait_);
-    }
-    return if_no_wait;
-  }
-
-  /** If we are taking a wait transition, and last transition wasn't a wait, we need to increment the number
-   *  of wait taken. On the opposite, if we took a wait before, and now we are taking another transition, we need
-   *  to decrease the count. */
-  void execute_next(aid_t aid, RemoteApp& app) override
-  {
-    auto& actor = actors_to_run_.at(aid);
-    if ((not taking_wait_) and is_transition_wait(actor.get_transition(actor.get_times_considered())->type_)) {
-      taken_wait_++;
-      taking_wait_ = true;
-      return;
-    }
-    if (taking_wait_ and (not is_transition_wait(actor.get_transition(actor.get_times_considered())->type_))) {
-      taken_wait_--;
-      taking_wait_ = false;
-      return;
-    }
-  }
-
-  void consider_best() override
-  {
-    const auto& [aid, _] = this->next_transition();
-    auto actor           = actors_to_run_.find(aid);
-    if (actor != actors_to_run_.end()) {
-      actor->second.mark_todo();
-      return;
-    }
-    for (auto& [_, actor] : actors_to_run_) {
-      if (actor.is_todo())
-        return;
-      if (actor.is_enabled() and not actor.is_done()) {
-        actor.mark_todo();
-        return;
-      }
-    }
-  }
-};
-
-} // namespace simgrid::mc
-
-#endif
index a1a51ce..a340c2c 100644 (file)
@@ -209,7 +209,7 @@ void CommDetExtension::enforce_deterministic_pattern(aid_t actor, const PatternC
       XBT_INFO("*********************************************************");
       XBT_INFO("%s", send_diff.c_str());
       exploration_.log_state();
-      exploration_.system_exit(SIMGRID_MC_EXIT_NON_DETERMINISM);
+      throw McError(ExitStatus::NON_DETERMINISM);
     } else if (_sg_mc_comms_determinism && (not send_deterministic && not recv_deterministic)) {
       XBT_INFO("****************************************************");
       XBT_INFO("***** Non-deterministic communications pattern *****");
@@ -219,7 +219,7 @@ void CommDetExtension::enforce_deterministic_pattern(aid_t actor, const PatternC
       if (not recv_diff.empty())
         XBT_INFO("%s", recv_diff.c_str());
       exploration_.log_state();
-      exploration_.system_exit(SIMGRID_MC_EXIT_NON_DETERMINISM);
+      throw McError(ExitStatus::NON_DETERMINISM);
     }
   }
 }
@@ -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 93c34fc..fef9cc7 100644 (file)
@@ -4,13 +4,16 @@
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
 #include "src/mc/explo/DFSExplorer.hpp"
-#include "src/mc/VisitedState.hpp"
 #include "src/mc/mc_config.hpp"
 #include "src/mc/mc_exit.hpp"
 #include "src/mc/mc_private.hpp"
 #include "src/mc/mc_record.hpp"
 #include "src/mc/transition/Transition.hpp"
 
+#if SIMGRID_HAVE_STATEFUL_MC
+#include "src/mc/VisitedState.hpp"
+#endif
+
 #include "src/xbt/mmalloc/mmprivate.h"
 #include "xbt/log.h"
 #include "xbt/string.hpp"
 #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");
@@ -41,6 +46,7 @@ xbt::signal<void(RemoteApp&)> DFSExplorer::on_log_state_signal;
 
 void DFSExplorer::check_non_termination(const State* current_state)
 {
+#if SIMGRID_HAVE_STATEFUL_MC
   for (auto const& state : stack_) {
     if (state->get_system_state()->equals_to(*current_state->get_system_state(),
                                              *get_remote_app().get_remote_process_memory())) {
@@ -56,43 +62,49 @@ void DFSExplorer::check_non_termination(const State* current_state)
                get_record_trace().to_string().c_str());
       log_state();
 
-      throw TerminationError();
+      throw McError(ExitStatus::NON_TERMINATION);
     }
   }
+#endif
 }
 
 RecordTrace DFSExplorer::get_record_trace() // override
 {
   RecordTrace res;
-  for (auto const& transition : stack_.back()->get_recipe())
-    res.push_back(transition);
-  res.push_back(stack_.back()->get_transition());
-  return res;
-}
 
-std::vector<std::string> DFSExplorer::get_textual_trace() // override
-{
-  std::vector<std::string> trace;
-  for (auto const& transition : stack_.back()->get_recipe()) {
-    trace.push_back(xbt::string_printf("%ld: %s", transition->aid_, transition->to_string().c_str()));
-  }
-  trace.push_back(xbt::string_printf("%ld: %s", stack_.back()->get_transition()->aid_,
-                                     stack_.back()->get_transition()->to_string().c_str()));
-  return trace;
+  if (const auto trans = stack_.back()->get_transition_out(); trans != nullptr)
+    res.push_back(trans.get());
+  for (const auto* state = stack_.back().get(); state != nullptr; state = state->get_parent_state().get())
+    if (state->get_transition_in() != nullptr)
+      res.push_front(state->get_transition_in().get());
+
+  return res;
 }
 
 void DFSExplorer::restore_stack(std::shared_ptr<State> state)
 {
-
-  stack_ = std::list<std::shared_ptr<State>>();
-  std::shared_ptr<State> current_state(state);
-  stack_.push_front(std::shared_ptr<State>(current_state));
+  stack_.clear();
+  execution_seq_     = odpor::Execution();
+  auto current_state = state;
+  stack_.emplace_front(current_state);
   // condition corresponds to reaching initial state
   while (current_state->get_parent_state() != nullptr) {
     current_state = current_state->get_parent_state();
-    stack_.push_front(std::shared_ptr<State>(current_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
@@ -100,8 +112,8 @@ void DFSExplorer::log_state() // override
   on_log_state_signal(get_remote_app());
   XBT_INFO("DFS exploration ended. %ld unique states visited; %lu backtracks (%lu transition replays, %lu states "
            "visited overall)",
-           State::get_expanded_states(), backtrack_count_, visited_states_count_,
-           Transition::get_replayed_transitions());
+           State::get_expanded_states(), backtrack_count_, Transition::get_replayed_transitions(),
+          visited_states_count_);
   Exploration::log_state();
 }
 
@@ -114,7 +126,7 @@ void DFSExplorer::run()
 
   while (not stack_.empty()) {
     /* Get current state */
-    std::shared_ptr<State> state(stack_.back());
+    auto state = stack_.back();
 
     XBT_DEBUG("**************************************************");
     XBT_DEBUG("Exploration depth=%zu (state:#%ld; %zu interleaves todo)", stack_.size(), state->get_num(),
@@ -128,12 +140,19 @@ 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;
     }
 
+#if SIMGRID_HAVE_STATEFUL_MC
     // Backtrack if we are revisiting a state we saw previously while applying state-equality reduction
     if (visited_state_ != nullptr) {
       XBT_DEBUG("State already visited (equal to state %ld), exploration stopped on this path.",
@@ -143,10 +162,27 @@ void DFSExplorer::run()
       this->backtrack();
       continue;
     }
+#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, double> 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(),
@@ -165,58 +201,65 @@ 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());
-    on_transition_execute_signal(state->get_transition(), 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
     // reached then perform one step of the exploration algorithm.
-    XBT_VERB("Execute %ld: %.60s (stack depth: %zu, state: %ld, %zu interleaves out of %zu enabled, %zu alternatives "
-             "to explore after)",
-             state->get_transition()->aid_, state->get_transition()->to_string().c_str(), stack_.size(),
-             state->get_num(), state->count_todo(), state->get_actor_count(), opened_states_.size());
+    XBT_VERB("Execute %ld: %.60s (stack depth: %zu, state: %ld, %zu interleaves)", state->get_transition_out()->aid_,
+             state->get_transition_out()->to_string().c_str(), stack_.size(), state->get_num(), state->count_todo());
 
     /* Create the new expanded state (copy the state of MCed into our MCer data) */
-    std::shared_ptr<State> next_state = std::make_shared<State>(get_remote_app(), state);
+    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()->to_string().c_str(), state->get_transition()->aid_);
-    state->add_sleep_set(state->get_transition()); // 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
      * on another process. If there exists one, find the more recent, and add its process to the interleave set.
      * If the process is not enabled at this  point, then add every enabled process to the interleave */
     if (reduction_mode_ == ReductionMode::dpor) {
-      aid_t issuer_id   = state->get_transition()->aid_;
-      stack_t tmp_stack = std::list(stack_);
+      aid_t issuer_id   = state->get_transition_out()->aid_;
+      stack_t tmp_stack = stack_;
       while (not tmp_stack.empty()) {
-        State* prev_state = tmp_stack.back().get();
-        if (state->get_transition()->aid_ == prev_state->get_transition()->aid_) {
-          XBT_DEBUG("Simcall >>%s<< and >>%s<< with same issuer %ld", state->get_transition()->to_string().c_str(),
-                    prev_state->get_transition()->to_string().c_str(), issuer_id);
+        if (const State* prev_state = tmp_stack.back().get();
+            state->get_transition_out()->aid_ == prev_state->get_transition_out()->aid_) {
+          XBT_DEBUG("Simcall >>%s<< and >>%s<< with same issuer %ld", state->get_transition_out()->to_string().c_str(),
+                    prev_state->get_transition_out()->to_string().c_str(), issuer_id);
           tmp_stack.pop_back();
           continue;
-        } else if (prev_state->get_transition()->depends(state->get_transition())) {
+        } else if (prev_state->get_transition_out()->depends(state->get_transition_out().get())) {
           XBT_VERB("Dependent Transitions:");
-          XBT_VERB("  %s (state=%ld)", prev_state->get_transition()->to_string().c_str(), prev_state->get_num());
-          XBT_VERB("  %s (state=%ld)", state->get_transition()->to_string().c_str(), state->get_num());
+          XBT_VERB("  %s (state=%ld)", prev_state->get_transition_out()->to_string().c_str(), prev_state->get_num());
+          XBT_VERB("  %s (state=%ld)", state->get_transition_out()->to_string().c_str(), state->get_num());
 
           if (prev_state->is_actor_enabled(issuer_id)) {
             if (not prev_state->is_actor_done(issuer_id)) {
               prev_state->consider_one(issuer_id);
-              if (not tmp_stack.back()->is_opened) {
-                opened_states_.push(std::shared_ptr<State>(tmp_stack.back()));
-                tmp_stack.back()->is_opened = true;
-              }
+              opened_states_.emplace_back(tmp_stack.back());
             } else
               XBT_DEBUG("Actor %ld is already in done set: no need to explore it again", issuer_id);
           } else {
@@ -224,36 +267,74 @@ void DFSExplorer::run()
                       "transition as todo",
                       issuer_id);
             // If we ended up marking at least a transition, explore it at some point
-            if (prev_state->consider_all() > 0 and not tmp_stack.back()->is_opened) {
-              opened_states_.push(std::shared_ptr<State>(tmp_stack.back()));
-              tmp_stack.back()->is_opened = true;
-            }
+            if (prev_state->consider_all() > 0)
+              opened_states_.emplace_back(tmp_stack.back());
           }
           break;
         } else {
           XBT_VERB("INDEPENDENT Transitions:");
-          XBT_VERB("  %s (state=%ld)", prev_state->get_transition()->to_string().c_str(), prev_state->get_num());
-          XBT_VERB("  %s (state=%ld)", state->get_transition()->to_string().c_str(), state->get_num());
+          XBT_VERB("  %s (state=%ld)", prev_state->get_transition_out()->to_string().c_str(), prev_state->get_num());
+          XBT_VERB("  %s (state=%ld)", state->get_transition_out()->to_string().c_str(), state->get_num());
         }
         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
     // need to give it to the opened states
-    if (stack_.back()->count_todo_multiples() > 0 and not stack_.back()->is_opened) {
-      opened_states_.push(std::shared_ptr<State>(stack_.back()));
-      stack_.back()->is_opened = true;
-    }
+    if (stack_.back()->count_todo_multiples() > 0)
+      opened_states_.emplace_back(stack_.back());
 
     if (_sg_mc_termination)
       this->check_non_termination(next_state.get());
 
-    /* Check whether we already explored next_state in the past (but only if interested in state-equality reduction) */
+#if SIMGRID_HAVE_STATEFUL_MC
+    /* 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
 
-    stack_.push_back(std::move(next_state));
+    stack_.emplace_back(std::move(next_state));
 
     /* If this is a new state (or if we don't care about state-equality reduction) */
     if (visited_state_ == nullptr) {
@@ -265,44 +346,143 @@ void DFSExplorer::run()
       }
 
       dot_output("\"%ld\" -> \"%ld\" [%s];\n", state->get_num(), stack_.back()->get_num(),
-                 state->get_transition()->dot_string().c_str());
-    } else
+                 state->get_transition_out()->dot_string().c_str());
+#if SIMGRID_HAVE_STATEFUL_MC
+    } else {
       dot_output("\"%ld\" -> \"%ld\" [%s];\n", state->get_num(),
                  visited_state_->original_num_ == -1 ? visited_state_->num_ : visited_state_->original_num_,
-                 state->get_transition()->dot_string().c_str());
+                 state->get_transition_out()->dot_string().c_str());
+#endif
+    }
   }
   log_state();
 }
 
+std::shared_ptr<State> DFSExplorer::best_opened_state()
+{
+  int best_prio = 0; // cache the value for the best priority found so far (initialized to silence gcc)
+  auto best     = end(opened_states_);   // iterator to the state to explore having the best priority
+  auto valid    = begin(opened_states_); // iterator marking the limit between states still to explore, and already
+                                         // explored ones
+
+  // Keep only still non-explored states (aid != -1), and record the one with the best (greater) priority.
+  for (auto current = begin(opened_states_); current != end(opened_states_); ++current) {
+    auto [aid, prio] = (*current)->next_transition_guided();
+    if (aid == -1)
+      continue;
+    if (valid != current)
+      *valid = std::move(*current);
+    if (best == end(opened_states_) || prio < best_prio) {
+      best_prio = prio;
+      best      = valid;
+    }
+    ++valid;
+  }
+
+  std::shared_ptr<State> best_state;
+  if (best < valid) {
+    // There are non-explored states, and one of them has the best priority.  Remove it from opened_states_ before
+    // returning.
+    best_state = std::move(*best);
+    --valid;
+    if (best != valid)
+      *best = std::move(*valid);
+  }
+  opened_states_.erase(valid, end(opened_states_));
+
+  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());
 
   on_backtracking_signal(get_remote_app());
   get_remote_app().check_deadlock();
 
+  // Take the point with smallest distance
+  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 (opened_states_.empty()) {
+  if (not backtracking_point) {
     XBT_DEBUG("No more opened point of exploration, the search will end");
-    stack_ = std::list<std::shared_ptr<State>>();
-    return;
-  }
-
-  std::shared_ptr<State> backtracking_point = opened_states_.top(); // Take the point with smallest distance
-  opened_states_.pop();
-
-  // if the smallest distance corresponded to no enable actor, remove this and let the
-  // exploration ask again for a backtrack
-  if (backtracking_point->next_transition_guided().first == -1) {
-    XBT_DEBUG("Best backtracking candidates has already been explored. Let's backtrack again");
-    this->backtrack();
+    stack_.clear();
     return;
   }
 
-  // We found a real backtracking point, let's go to it
+  // We found a backtracking point, let's go to it
   backtrack_count_++;
   XBT_DEBUG("Backtracking to state#%ld", backtracking_point->get_num());
+
+#if SIMGRID_HAVE_STATEFUL_MC
   /* If asked to rollback on a state that has a snapshot, restore it */
   if (const auto* system_state = backtracking_point->get_system_state()) {
     system_state->restore(*get_remote_app().get_remote_process_memory());
@@ -310,32 +490,54 @@ void DFSExplorer::backtrack()
     this->restore_stack(backtracking_point);
     return;
   }
+#endif
+
+  // Search how to restore the backtracking point
+  State* init_state = nullptr;
+  std::deque<Transition*> replay_recipe;
+  for (auto* s = backtracking_point.get(); s != nullptr; s = s->get_parent_state().get()) {
+#if SIMGRID_HAVE_STATEFUL_MC
+    if (s->get_system_state() != nullptr) { // Found a state that I can restore
+      init_state = s;
+      break;
+    }
+#endif
+    if (s->get_transition_in() != nullptr) // The root has no transition_in
+      replay_recipe.push_front(s->get_transition_in().get());
+  }
+
+  // Restore the init_state, if any
+  if (init_state != nullptr) {
+#if SIMGRID_HAVE_STATEFUL_MC
+    const auto* system_state = init_state->get_system_state();
+    system_state->restore(*get_remote_app().get_remote_process_memory());
+    on_restore_system_state_signal(init_state, get_remote_app());
+#endif
+  } else { // Restore the initial state if no intermediate state was found
+    get_remote_app().restore_initial_state();
+    on_restore_initial_state_signal(get_remote_app());
+  }
 
   /* if no snapshot, we need to restore the initial state and replay the transitions */
-  get_remote_app().restore_initial_state();
-  on_restore_initial_state_signal(get_remote_app());
   /* Traverse the stack from the state at position start and re-execute the transitions */
-  long i = 1;
-  for (auto& transition : backtracking_point->get_recipe()) {
+  for (auto& transition : replay_recipe) {
     transition->replay(get_remote_app());
     on_transition_replay_signal(transition, get_remote_app());
-    XBT_DEBUG("[Backtracking in progress] executing state #%ld/#%ld", i, backtracking_point->get_num());
     visited_states_count_++;
-    i++;
   }
   this->restore_stack(backtracking_point);
 }
 
-DFSExplorer::DFSExplorer(const std::vector<char*>& args, bool with_dpor, bool need_memory_info)
-    : Exploration(args, need_memory_info || _sg_mc_termination)
+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 {
@@ -348,7 +550,7 @@ DFSExplorer::DFSExplorer(const std::vector<char*>& args, bool with_dpor, bool ne
 
   XBT_DEBUG("**************************************************");
 
-  stack_.push_back(std::move(initial_state));
+  stack_.emplace_back(std::move(initial_state));
 
   /* Get an enabled actor and insert it in the interleave set of the initial state */
   XBT_DEBUG("Initial state. %lu actors to consider", stack_.back()->get_actor_count());
@@ -358,12 +560,20 @@ DFSExplorer::DFSExplorer(const std::vector<char*>& args, bool with_dpor, bool ne
     stack_.back()->consider_all();
   }
   if (stack_.back()->count_todo_multiples() > 1)
-    opened_states_.push(std::shared_ptr<State>(stack_.back()));
+    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 c709edd..a0dc478 100644 (file)
@@ -6,32 +6,30 @@
 #ifndef SIMGRID_MC_SAFETY_CHECKER_HPP
 #define SIMGRID_MC_SAFETY_CHECKER_HPP
 
-#include "src/mc/VisitedState.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 {
 
-typedef std::list<std::shared_ptr<State>> stack_t;
-
-/* Used to compare two stacks and decide which one is better to backtrack,
- * regarding the chosen guide in the last state. */
-class OpenedStatesCompare {
-public:
-  bool operator()(std::shared_ptr<State> const& lhs, std::shared_ptr<State> const& rhs)
-  {
-    return lhs->next_transition_guided().second < rhs->next_transition_guided().second;
-  }
-};
+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
@@ -49,10 +47,9 @@ 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;
-  std::vector<std::string> get_textual_trace() override;
   void log_state() override;
 
   /** Called once when the exploration starts */
@@ -100,13 +97,32 @@ 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_;
+#else
+  void* visited_state_ = nullptr; /* The code uses it to detect whether we are doing stateful MC */
+#endif
 
   /** Opened states are states that still contains todo actors.
    *  When backtracking, we pick a state from it*/
+  std::vector<std::shared_ptr<State>> opened_states_;
+  std::shared_ptr<State> best_opened_state();
 
-  std::priority_queue<std::shared_ptr<State>, std::vector<std::shared_ptr<State>>, OpenedStatesCompare> opened_states_;
+  /** 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
index 061a9b4..8bf0110 100644 (file)
@@ -5,9 +5,14 @@
 
 #include "src/mc/explo/Exploration.hpp"
 #include "src/mc/mc_config.hpp"
+#include "src/mc/mc_environ.h"
 #include "src/mc/mc_exit.hpp"
 #include "src/mc/mc_private.hpp"
+#include "xbt/string.hpp"
+
+#if SIMGRID_HAVE_STATEFUL_MC
 #include "src/mc/sosp/RemoteProcessMemory.hpp"
+#endif
 
 #include <sys/wait.h>
 
@@ -59,20 +64,40 @@ void Exploration::log_state()
     dot_output("}\n");
     fclose(dot_output_);
   }
-  if (getenv("SIMGRID_MC_SYSTEM_STATISTICS")) {
+  if (getenv(MC_ENV_SYSTEM_STATISTICS)) {
     int ret = system("free");
     if (ret != 0)
       XBT_WARN("Call to system(free) did not return 0, but %d", ret);
   }
 }
+// Make our tests fully reproducible despite the subtle differences of strsignal() across archs
+static const char* signal_name(int status)
+{
+  switch (WTERMSIG(status)) {
+    case SIGABRT: // FreeBSD uses "Abort trap" as a strsignal for SIGABRT
+      return "Aborted";
+    case SIGSEGV: // MacOSX uses "Segmentation fault: 11" for SIGKILL
+      return "Segmentation fault";
+    default:
+      return strsignal(WTERMSIG(status));
+  }
+}
+
+std::vector<std::string> Exploration::get_textual_trace()
+{
+  std::vector<std::string> trace;
+  for (auto const& transition : get_record_trace())
+    trace.push_back(xbt::string_printf("%ld: %s", transition->aid_, transition->to_string().c_str()));
+  return trace;
+}
 
 XBT_ATTRIB_NORETURN void Exploration::report_crash(int status)
 {
   XBT_INFO("**************************");
   XBT_INFO("** CRASH IN THE PROGRAM **");
   XBT_INFO("**************************");
-  if (WIFSIGNALED(status)) // FreeBSD use "Abort trap" as a strsignal for SIGABRT that is part of our tests
-    XBT_INFO("From signal: %s", WTERMSIG(status) == SIGABRT ? "Aborted" : strsignal(WTERMSIG(status)));
+  if (WIFSIGNALED(status))
+    XBT_INFO("From signal: %s", signal_name(status));
   else if (WIFEXITED(status))
     XBT_INFO("From exit: %i", WEXITSTATUS(status));
   if (not xbt_log_no_loc)
@@ -88,16 +113,17 @@ XBT_ATTRIB_NORETURN void Exploration::report_crash(int status)
   if (xbt_log_no_loc) {
     XBT_INFO("Stack trace not displayed because you passed --log=no_loc");
   } else {
+#if SIMGRID_HAVE_STATEFUL_MC
     const auto* memory = get_remote_app().get_remote_process_memory();
     if (memory) {
       XBT_INFO("Stack trace:");
       memory->dump_stack();
-    } else {
+    } else
+#endif
       XBT_INFO("Stack trace not shown because there is no memory introspection.");
-    }
   }
 
-  system_exit(SIMGRID_MC_EXIT_PROGRAM_CRASH);
+  throw McError(ExitStatus::PROGRAM_CRASH);
 }
 XBT_ATTRIB_NORETURN void Exploration::report_assertion_failure()
 {
@@ -111,12 +137,7 @@ XBT_ATTRIB_NORETURN void Exploration::report_assertion_failure()
            "--cfg=model-check/replay:'%s'",
            get_record_trace().to_string().c_str());
   log_state();
-  system_exit(SIMGRID_MC_EXIT_SAFETY);
-}
-
-void Exploration::system_exit(int status) const
-{
-  ::exit(status);
+  throw McError(ExitStatus::SAFETY);
 }
 
 }; // namespace simgrid::mc
index aa6d420..64114d4 100644 (file)
@@ -8,6 +8,8 @@
 
 #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>
 
@@ -39,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 */
@@ -50,20 +52,15 @@ public:
   /** Produce an error message indicating that a property was violated */
   XBT_ATTRIB_NORETURN void report_assertion_failure();
 
-  /** Kill the application and the model-checker (which exits with `status`)*/
-  XBT_ATTRIB_NORETURN void system_exit(int status) const;
-
   /* These methods are callbacks called by the model-checking engine
    * to get and display information about the current state of the
    * model-checking algorithm: */
 
-  /** Show the current trace/stack
-   *
-   *  Could this be handled in the Session/ModelChecker instead? */
+  /** Retrieve the current stack to build an execution trace */
   virtual RecordTrace get_record_trace() = 0;
 
   /** Generate a textual execution trace of the simulated application */
-  virtual std::vector<std::string> get_textual_trace() = 0;
+  std::vector<std::string> get_textual_trace();
 
   /** Log additional information about the state of the model-checker */
   virtual void log_state();
@@ -76,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 3638fd6..3d60ef9 100644 (file)
@@ -120,8 +120,8 @@ void LivenessChecker::replay()
     std::shared_ptr<State> state = pair->app_state_;
 
     if (pair->exploration_started) {
-      state->get_transition()->replay(get_remote_app());
-      XBT_DEBUG("Replay (depth = %d) : %s (%p)", depth, state->get_transition()->to_string().c_str(), state.get());
+      state->get_transition_out()->replay(get_remote_app());
+      XBT_DEBUG("Replay (depth = %d) : %s (%p)", depth, state->get_transition_out()->to_string().c_str(), state.get());
     }
 
     /* Update statistics */
@@ -258,7 +258,8 @@ RecordTrace LivenessChecker::get_record_trace() // override
 {
   RecordTrace res;
   for (std::shared_ptr<Pair> const& pair : exploration_stack_)
-    res.push_back(pair->app_state_->get_transition());
+    if (pair->app_state_->get_transition_out() != nullptr)
+      res.push_back(pair->app_state_->get_transition_out().get());
   return res;
 }
 
@@ -285,15 +286,6 @@ void LivenessChecker::show_acceptance_cycle(std::size_t depth)
   XBT_INFO("Counter-example depth: %zu", depth);
 }
 
-std::vector<std::string> LivenessChecker::get_textual_trace() // override
-{
-  std::vector<std::string> trace;
-  for (std::shared_ptr<Pair> const& pair : exploration_stack_)
-    trace.push_back(pair->app_state_->get_transition()->to_string());
-
-  return trace;
-}
-
 std::shared_ptr<Pair> LivenessChecker::create_pair(const Pair* current_pair, xbt_automaton_state_t state,
                                                    std::shared_ptr<const std::vector<int>> propositions)
 {
@@ -384,7 +376,7 @@ void LivenessChecker::run()
       reached_pair = this->insert_acceptance_pair(current_pair.get());
       if (reached_pair == nullptr) {
         this->show_acceptance_cycle(current_pair->depth);
-        throw LivenessError();
+        throw McError(ExitStatus::LIVENESS);
       }
     }
 
@@ -402,7 +394,7 @@ void LivenessChecker::run()
     }
 
     current_pair->app_state_->execute_next(current_pair->app_state_->next_transition(), get_remote_app());
-    XBT_DEBUG("Execute: %s", current_pair->app_state_->get_transition()->to_string().c_str());
+    XBT_DEBUG("Execute: %s", current_pair->app_state_->get_transition_out()->to_string().c_str());
 
     /* Update the dot output */
     if (this->previous_pair_ != 0 && this->previous_pair_ != current_pair->num) {
@@ -410,7 +402,7 @@ void LivenessChecker::run()
       this->previous_request_.clear();
     }
     this->previous_pair_    = current_pair->num;
-    this->previous_request_ = current_pair->app_state_->get_transition()->dot_string();
+    this->previous_request_ = current_pair->app_state_->get_transition_out()->dot_string();
     if (current_pair->search_cycle)
       dot_output("%d [shape=doublecircle];\n", current_pair->num);
 
index 532f639..baed48e 100644 (file)
@@ -56,7 +56,6 @@ public:
 
   void run() override;
   RecordTrace get_record_trace() override;
-  std::vector<std::string> get_textual_trace() override;
   void log_state() override;
 
 private:
index 98bab5e..e367890 100644 (file)
@@ -6,46 +6,44 @@
 #include "src/mc/explo/UdporChecker.hpp"
 #include "src/mc/api/State.hpp"
 #include "src/mc/explo/udpor/Comb.hpp"
+#include "src/mc/explo/udpor/ExtensionSetCalculator.hpp"
 #include "src/mc/explo/udpor/History.hpp"
 #include "src/mc/explo/udpor/maximal_subsets_iterator.hpp"
 
+#include <numeric>
 #include <xbt/asserts.h>
 #include <xbt/log.h>
+#include <xbt/string.hpp>
 
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_udpor, mc, "Logging specific to verification using UDPOR");
 
 namespace simgrid::mc::udpor {
 
-UdporChecker::UdporChecker(const std::vector<char*>& args) : Exploration(args, true)
-{
-  // Initialize the map
-}
+UdporChecker::UdporChecker(const std::vector<char*>& args) : Exploration(args, true) {}
 
 void UdporChecker::run()
 {
   XBT_INFO("Starting a UDPOR exploration");
-  // NOTE: `A`, `D`, and `C` are derived from the
-  // original UDPOR paper [1], while `prev_exC` arises
-  // from the incremental computation of ex(C) from [3]
-  Configuration C_root;
-
-  // TODO: Move computing the root configuration into a method on the Unfolding
-  auto initial_state      = get_current_state();
-  auto root_event         = std::make_unique<UnfoldingEvent>(EventSet(), std::make_shared<Transition>());
-  auto* root_event_handle = root_event.get();
-  unfolding.insert(std::move(root_event));
-  C_root.add_event(root_event_handle);
-
-  explore(C_root, EventSet(), EventSet(), std::move(initial_state), EventSet());
-
+  state_stack.clear();
+  state_stack.push_back(get_current_state());
+  explore(Configuration(), EventSet(), EventSet(), EventSet());
   XBT_INFO("UDPOR exploration terminated -- model checking completed");
 }
 
-void UdporChecker::explore(const Configuration& C, EventSet D, EventSet A, std::unique_ptr<State> stateC,
-                           EventSet prev_exC)
+void UdporChecker::explore(const Configuration& C, EventSet D, EventSet A, EventSet prev_exC)
 {
-  auto exC       = compute_exC(C, *stateC, prev_exC);
+  auto& stateC   = *state_stack.back();
+  auto exC       = compute_exC(C, stateC, prev_exC);
   const auto enC = compute_enC(C, exC);
+  XBT_DEBUG("explore(C, D, A) with:\n"
+            "C\t := %s \n"
+            "D\t := %s \n"
+            "A\t := %s \n"
+            "ex(C)\t := %s \n"
+            "en(C)\t := %s \n",
+            C.to_string().c_str(), D.to_string().c_str(), A.to_string().c_str(), exC.to_string().c_str(),
+            enC.to_string().c_str());
+  XBT_DEBUG("ex(C) has %zu elements, of which %zu are in en(C)", exC.size(), enC.size());
 
   // If enC is a subset of D, intuitively
   // there aren't any enabled transitions
@@ -53,9 +51,10 @@ void UdporChecker::explore(const Configuration& C, EventSet D, EventSet A, std::
   // exploration would lead to a so-called
   // "sleep-set blocked" trace.
   if (enC.is_subset_of(D)) {
-    if (not C.get_events().empty()) {
-      // Report information...
-    }
+    XBT_DEBUG("en(C) is a subset of the sleep set D (size %zu); if we "
+              "kept exploring, we'd hit a sleep-set blocked trace",
+              D.size());
+    XBT_DEBUG("The current configuration has %zu elements", C.get_events().size());
 
     // When `en(C)` is empty, intuitively this means that there
     // are no enabled transitions that can be executed from the
@@ -65,23 +64,19 @@ void UdporChecker::explore(const Configuration& C, EventSet D, EventSet A, std::
     // possibility is that we've finished running everything, and
     // we wouldn't be in deadlock then)
     if (enC.empty()) {
+      XBT_VERB("Maximal configuration detected. Checking for deadlock...");
       get_remote_app().check_deadlock();
     }
 
     return;
   }
-
-  // TODO: Add verbose logging about which event is being explored
-
-  const UnfoldingEvent* e = select_next_unfolding_event(A, enC);
+  UnfoldingEvent* e = select_next_unfolding_event(A, enC);
   xbt_assert(e != nullptr, "\n\n****** INVARIANT VIOLATION ******\n"
                            "UDPOR guarantees that an event will be chosen at each point in\n"
                            "the search, yet no events were actually chosen\n"
                            "*********************************\n\n");
-
-  // Move the application into stateCe and make note of that state
-  move_to_stateCe(*stateC, *e);
-  auto stateCe = record_current_state();
+  XBT_DEBUG("Selected event `%s` (%zu dependencies) to extend the configuration", e->to_string().c_str(),
+            e->get_immediate_causes().size());
 
   // Ce := C + {e}
   Configuration Ce = C;
@@ -91,22 +86,48 @@ void UdporChecker::explore(const Configuration& C, EventSet D, EventSet A, std::
   exC.remove(e);
 
   // Explore(C + {e}, D, A \ {e})
-  explore(Ce, D, std::move(A), std::move(stateCe), std::move(exC));
+
+  // Move the application into stateCe (i.e. `state(C + {e})`) and make note of that state
+  move_to_stateCe(&stateC, e);
+  state_stack.push_back(record_current_state());
+
+  explore(Ce, D, std::move(A), std::move(exC));
+
+  // Prepare to move the application back one state.
+  // We need only remove the state from the stack here: if we perform
+  // another `Explore()` after computing an alternative, at that
+  // point we'll actually create a fresh RemoteProcess
+  state_stack.pop_back();
 
   // D <-- D + {e}
   D.insert(e);
 
-  constexpr unsigned K = 10;
-  if (auto J = C.compute_k_partial_alternative_to(D, this->unfolding, K); J.has_value()) {
+  XBT_DEBUG("Checking for the existence of an alternative...");
+  if (auto J = C.compute_alternative_to(D, this->unfolding); J.has_value()) {
     // Before searching the "right half", we need to make
     // sure the program actually reflects the fact
-    // that we are searching again from `stateC` (the recursive
-    // search moved the program into `stateCe`)
-    restore_program_state_to(*stateC);
+    // that we are searching again from `state(C)`. While the
+    // stack of states is properly adjusted to represent
+    // `state(C)` all together, the RemoteApp is currently sitting
+    // at some *future* state with resepct to `state(C)` since the
+    // recursive calls have moved it there.
+    restore_program_state_with_current_stack();
 
     // Explore(C, D + {e}, J \ C)
     auto J_minus_C = J.value().get_events().subtracting(C.get_events());
-    explore(C, D, std::move(J_minus_C), std::move(stateC), std::move(prev_exC));
+
+    XBT_DEBUG("Alternative detected! The alternative is:\n"
+              "J\t := %s \n"
+              "J / C := %s\n"
+              "UDPOR is going to explore it...",
+              J.value().to_string().c_str(), J_minus_C.to_string().c_str());
+    explore(C, D, std::move(J_minus_C), std::move(prev_exC));
+  } else {
+    XBT_DEBUG("No alternative detected with:\n"
+              "C\t := %s \n"
+              "D\t := %s \n"
+              "A\t := %s \n",
+              C.to_string().c_str(), D.to_string().c_str(), A.to_string().c_str());
   }
 
   // D <-- D - {e}
@@ -131,65 +152,28 @@ EventSet UdporChecker::compute_exC(const Configuration& C, const State& stateC,
 
   for (const auto& [aid, actor_state] : stateC.get_actors_list()) {
     for (const auto& transition : actor_state.get_enabled_transitions()) {
-      // First check for a specialized function that can compute the extension
-      // set "quickly" based on its type. Otherwise, fall back to computing
-      // the set "by hand"
-      const auto specialized_extension_function = incremental_extension_functions.find(transition->type_);
-      if (specialized_extension_function != incremental_extension_functions.end()) {
-        exC.form_union((specialized_extension_function->second)(C, transition));
-      } else {
-        exC.form_union(this->compute_exC_by_enumeration(C, transition));
-      }
+      XBT_DEBUG("\t Considering partial extension for %s", transition->to_string().c_str());
+      EventSet extension = ExtensionSetCalculator::partially_extend(C, &unfolding, transition);
+      exC.form_union(extension);
     }
   }
   return exC;
 }
 
-EventSet UdporChecker::compute_exC_by_enumeration(const Configuration& C, const std::shared_ptr<Transition> action)
-{
-  // Here we're computing the following:
-  //
-  // U{<a, K> : K is maximal, `a` depends on all of K, `a` enabled at config(K) }
-  //
-  // where `a` is the `action` given to us. Note that `a` is presumed to be enabled
-  EventSet incremental_exC;
-
-  for (auto begin =
-           maximal_subsets_iterator(C, {[&](const UnfoldingEvent* e) { return e->is_dependent_with(action.get()); }});
-       begin != maximal_subsets_iterator(); ++begin) {
-    const EventSet& maximal_subset = *begin;
-
-    // Determining if `a` is enabled here might not be possible while looking at `a` opaquely
-    // We leave the implementation as-is to ensure that any addition would be simple
-    // if it were ever added
-    const bool enabled_at_config_k = false;
-
-    if (enabled_at_config_k) {
-      auto candidate_handle = std::make_unique<UnfoldingEvent>(maximal_subset, action);
-      if (auto candidate_event = candidate_handle.get(); not unfolding.contains_event_equivalent_to(candidate_event)) {
-        // This is a new event (i.e. one we haven't yet seen)
-        unfolding.insert(std::move(candidate_handle));
-        incremental_exC.insert(candidate_event);
-      }
-    }
-  }
-  return incremental_exC;
-}
-
 EventSet UdporChecker::compute_enC(const Configuration& C, const EventSet& exC) const
 {
   EventSet enC;
   for (const auto e : exC) {
-    if (not e->conflicts_with(C)) {
+    if (C.is_compatible_with(e)) {
       enC.insert(e);
     }
   }
   return enC;
 }
 
-void UdporChecker::move_to_stateCe(State& state, const UnfoldingEvent& e)
+void UdporChecker::move_to_stateCe(State* state, UnfoldingEvent* e)
 {
-  const aid_t next_actor = e.get_transition()->aid_;
+  const aid_t next_actor = e->get_transition()->aid_;
 
   // TODO: Add the trace if possible for reporting a bug
   xbt_assert(next_actor >= 0, "\n\n****** INVARIANT VIOLATION ******\n"
@@ -197,15 +181,41 @@ void UdporChecker::move_to_stateCe(State& state, const UnfoldingEvent& e)
                               "one transition of the state of an visited event is enabled, yet no\n"
                               "state was actually enabled. Please report this as a bug.\n"
                               "*********************************\n\n");
-  state.execute_next(next_actor, get_remote_app());
+  auto latest_transition_by_next_actor = state->execute_next(next_actor, get_remote_app());
+
+  // The transition that is associated with the event was just
+  // executed, so it's possible that the new version of the transition
+  // (i.e. the one after execution) has *more* information than
+  // that which existed *prior* to execution.
+  //
+  //
+  // ------- !!!!! UDPOR INVARIANT !!!!! -------
+  //
+  // At this point, we are leveraging the fact that
+  // UDPOR will not contain more than one copy of any
+  // transition executed by any actor for any
+  // particular step taken by that actor. That is,
+  // if transition `i` of the `j`th actor is contained in the
+  // configuration `C` currently under consideration
+  // by UDPOR, then only one and only one copy exists in `C`
+  //
+  // This means that we can referesh the transitions associated
+  // with each event lazily, i.e. only after we have chosen the
+  // event to continue our execution.
+  e->set_transition(std::move(latest_transition_by_next_actor));
 }
 
-void UdporChecker::restore_program_state_to(const State& stateC)
+void UdporChecker::restore_program_state_with_current_stack()
 {
+  XBT_DEBUG("Restoring state using the current stack");
   get_remote_app().restore_initial_state();
-  // TODO: We need to have the stack of past states available at this
-  // point. Since the method is recursive, we'll need to keep track of
-  // this as we progress
+
+  /* Traverse the stack from the state at position start and re-execute the transitions */
+  for (const std::unique_ptr<State>& state : state_stack) {
+    if (state == state_stack.back()) /* If we are arrived on the target state, don't replay the outgoing transition */
+      break;
+    state->get_transition_out()->replay(get_remote_app());
+  }
 }
 
 std::unique_ptr<State> UdporChecker::record_current_state()
@@ -218,15 +228,21 @@ std::unique_ptr<State> UdporChecker::record_current_state()
   return next_state;
 }
 
-const UnfoldingEvent* UdporChecker::select_next_unfolding_event(const EventSet& A, const EventSet& enC)
+UnfoldingEvent* UdporChecker::select_next_unfolding_event(const EventSet& A, const EventSet& enC)
 {
-  if (!enC.empty()) {
-    return *(enC.begin());
+  if (enC.empty()) {
+    throw std::invalid_argument("There are no unfolding events to select. "
+                                "Are you sure that you checked that en(C) was not "
+                                "empty before attempting to select an event from it?");
+  }
+
+  if (A.empty()) {
+    return const_cast<UnfoldingEvent*>(*(enC.begin()));
   }
 
   for (const auto& event : A) {
     if (enC.contains(event)) {
-      return event;
+      return const_cast<UnfoldingEvent*>(event);
     }
   }
   return nullptr;
@@ -234,37 +250,81 @@ const UnfoldingEvent* UdporChecker::select_next_unfolding_event(const EventSet&
 
 void UdporChecker::clean_up_explore(const UnfoldingEvent* e, const Configuration& C, const EventSet& D)
 {
-  const EventSet C_union_D              = C.get_events().make_union(D);
-  const EventSet es_immediate_conflicts = this->unfolding.get_immediate_conflicts_of(e);
-  const EventSet Q_CDU                  = C_union_D.make_union(es_immediate_conflicts.get_local_config());
+  // The "clean-up set" conceptually represents
+  // those events which will no longer be considered
+  // by UDPOR during its exploration. The concept is
+  // introduced to avoid modification during iteration
+  // over the current unfolding to determine who needs to
+  // be removed. Since sets are unordered, it's quite possible
+  // that e.g. two events `e` and `e'` such that `e < e'`
+  // which are determined eligible for removal are removed
+  // in the order `e` and then `e'`. Determining that `e'`
+  // needs to be removed requires that its history be in
+  // tact to e.g. compute the conflicts with the event.
+  //
+  // Thus, we compute the set and remove all of the events
+  // at once in lieu of removing events while iterating over them.
+  // We can hypothesize that processing the events in reverse
+  // topological order would prevent any issues concerning
+  // the order in which are processed
+  EventSet clean_up_set;
+
+  // Q_(C, D, U) = C u D u U (complicated expression)
+  // See page 9 of "Unfolding-based Partial Order Reduction"
+
+  // "C u D" portion
+  const EventSet C_union_D = C.get_events().make_union(D);
+
+  // "U (complicated expression)" portion
+  const EventSet conflict_union = std::accumulate(
+      C_union_D.begin(), C_union_D.end(), EventSet(), [&](const EventSet acc, const UnfoldingEvent* e_prime) {
+        return acc.make_union(unfolding.get_immediate_conflicts_of(e_prime));
+      });
+
+  const EventSet Q_CDU = C_union_D.make_union(conflict_union.get_local_config());
+
+  XBT_DEBUG("Computed Q_CDU as '%s'", Q_CDU.to_string().c_str());
 
   // Move {e} \ Q_CDU from U to G
-  if (Q_CDU.contains(e)) {
-    this->unfolding.remove(e);
+  if (not Q_CDU.contains(e)) {
+    XBT_DEBUG("Moving %s from U to G...", e->to_string().c_str());
+    clean_up_set.insert(e);
   }
 
   // foreach ê in #ⁱ_U(e)
-  for (const auto* e_hat : es_immediate_conflicts) {
+  for (const auto* e_hat : this->unfolding.get_immediate_conflicts_of(e)) {
     // Move [ê] \ Q_CDU from U to G
-    const EventSet to_remove = e_hat->get_history().subtracting(Q_CDU);
-    this->unfolding.remove(to_remove);
+    const EventSet to_remove = e_hat->get_local_config().subtracting(Q_CDU);
+    XBT_DEBUG("Moving {%s} from U to G...", to_remove.to_string().c_str());
+    clean_up_set.form_union(to_remove);
   }
+
+  // TODO: We still perhaps need to
+  // figure out how to deal with the fact that the previous
+  // extension sets computed for past configurations
+  // contain events that may be removed from `U`. Perhaps
+  // it would be best to keep them around forever (they
+  // are moved to `G` after all and can be discarded at will,
+  // which means they may never have to be removed at all).
+  //
+  // Of course, the benefit of moving them into the set `G`
+  // is that the computation for immediate conflicts becomes
+  // more efficient (we have to search all of `U` for such conflicts,
+  // and there would be no reason to search those events
+  // that UDPOR has marked as no longer being important)
+  // For now, there appear to be no "obvious" issues (although
+  // UDPOR's behavior is often far from obvious...)
+  this->unfolding.mark_finished(clean_up_set);
 }
 
 RecordTrace UdporChecker::get_record_trace()
 {
   RecordTrace res;
+  for (auto const& state : state_stack)
+    res.push_back(state->get_transition_out().get());
   return res;
 }
 
-std::vector<std::string> UdporChecker::get_textual_trace()
-{
-  // TODO: Topologically sort the events of the latest configuration
-  // and iterate through that topological sorting
-  std::vector<std::string> trace;
-  return trace;
-}
-
 } // namespace simgrid::mc::udpor
 
 namespace simgrid::mc {
index 6214039..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"
@@ -15,6 +16,7 @@
 #include "src/mc/mc_record.hpp"
 
 #include <functional>
+#include <list>
 #include <optional>
 
 namespace simgrid::mc::udpor {
@@ -37,20 +39,14 @@ public:
 
   void run() override;
   RecordTrace get_record_trace() override;
-  std::vector<std::string> get_textual_trace() override;
-
-  inline std::unique_ptr<State> get_current_state() { return std::make_unique<State>(get_remote_app()); }
+  std::unique_ptr<State> get_current_state() { return std::make_unique<State>(get_remote_app()); }
 
 private:
   Unfolding unfolding = Unfolding();
 
-  /**
-   * @brief A collection of specialized functions which can incrementally
-   * compute the extension of a configuration based on the action taken
-   */
-  using ExtensionFunction = std::function<EventSet(const Configuration&, const std::shared_ptr<Transition>)>;
-  std::unordered_map<Transition::Type, ExtensionFunction> incremental_extension_functions =
-      std::unordered_map<Transition::Type, ExtensionFunction>();
+  // The current sequence of states that the checker has
+  // visited in order to reach the current configuration
+  std::list<std::unique_ptr<State>> state_stack;
 
   /**
    * @brief Explores the unfolding of the concurrent system
@@ -67,13 +63,11 @@ private:
    * @param A the set of events to "guide" UDPOR in the correct direction
    * when it returns back to a node in the unfolding and must decide among
    * events to select from `ex(C)`. See [1] for more details
-   * @param stateC the state of the program after having executed `C`,
-   * viz. `state(C)`  using the notation of [1]
    *
    * TODO: Add the optimization where we can check if e == e_prior
    * to prevent repeated work when computing ex(C)
    */
-  void explore(const Configuration& C, EventSet D, EventSet A, std::unique_ptr<State> stateC, EventSet prev_exC);
+  void explore(const Configuration& C, EventSet D, EventSet A, EventSet prev_exC);
 
   /**
    * @brief Identifies the next event from the unfolding of the concurrent system
@@ -87,7 +81,7 @@ private:
    * by the UDPOR algorithm to select new events to search. See the original
    * paper [1] for more details
    */
-  const UnfoldingEvent* select_next_unfolding_event(const EventSet& A, const EventSet& enC);
+  UnfoldingEvent* select_next_unfolding_event(const EventSet& A, const EventSet& enC);
 
   /**
    * @brief Computes the sets `ex(C)` and `en(C)` of the given configuration
@@ -114,38 +108,43 @@ private:
    * @returns the extension set `ex(C)` of `C`
    */
   EventSet compute_exC(const Configuration& C, const State& stateC, const EventSet& prev_exC);
-
-  /**
-   * @brief Computes a portion of the extension set of a configuration given
-   * some action `action` by directly enumerating all maximal subsets of C
-   * (i.e. without specializations based on the action)
-   */
-  EventSet compute_exC_by_enumeration(const Configuration& C, const std::shared_ptr<Transition> action);
-
   EventSet compute_enC(const Configuration& C, const EventSet& exC) const;
 
   /**
    *
    */
-  void move_to_stateCe(State& stateC, const UnfoldingEvent& e);
+  void move_to_stateCe(State* stateC, UnfoldingEvent* e);
 
   /**
-   * @brief Creates a new snapshot of the state of the progam undergoing
-   * model checking
-   *
-   * @returns the handle used to uniquely identify this state later in the
-   * exploration of the unfolding. You provide this handle to an event in the
-   * unfolding to regenerate past states
+   * @brief Creates a new snapshot of the state of the application
+   * as it currently looks
    */
   std::unique_ptr<State> record_current_state();
 
   /**
+   * @brief Move the application side into the state at the top of the
+   * state stack provided
+   *
+   * As UDPOR performs its search, it moves the application-side along with
+   * it so that the application is always providing the checker with
+   * the correct information about what each actor is running (and whether
+   * those actors are enabled) at the state reached by the configuration it
+   * decides to search.
    *
+   * When UDPOR decides to "backtrack" (e.g. after reaching a configuration
+   * whose en(C) is empty), before it attempts to continue the search by taking
+   * a different path from a configuration it visited in the past, it must ensure
+   * that the application-side has moved back into `state(C)`.
+   *
+   * The search may have moved the application arbitrarily deep into its execution,
+   * and the search may backtrack arbitrarily closer to the beginning of the execution.
+   * The UDPOR implementation in SimGrid ensures that the stack is updated appropriately,
+   * but the process must still be regenerated.
    */
-  void restore_program_state_to(const State& stateC);
+  void restore_program_state_with_current_stack();
 
   /**
-   *
+   * @brief Perform the functionality of the `Remove(e, C, D)` function in [1]
    */
   void clean_up_explore(const UnfoldingEvent* e, const Configuration& C, const EventSet& D);
 };
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..6d8ca42
--- /dev/null
@@ -0,0 +1,365 @@
+/* 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 the set `I_[E'](v)` if condition on line 8 holds.
+   * 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.
+   *
+   * @precondition: 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 a set of actors not already contained in `backtrack_set`
+   * which serve as an initials to reverse the race between `e`
+   * and `e' := get_latest_event_handle()`; that is, an initial that is
+   * not already contained in the set `backtrack_set`.
+   */
+  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 sleep 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
+   * 7 |   --> wut(E') := insert_[E'](v, wut(E'))
+   *
+   * The sequence `v` is computed and returned as needed, based on whether
+   * the check on line 6 passes.
+   *
+   * @precondition: This method assumes that events `e` and
+   * `e_prime` are in a *reversible* race, as is the case
+   * in ODPOR.
+   *
+   * @returns a partial execution `v := notdep(e, E)` (where `E` refers
+   * to this execution) that should be inserted into a wakeup tree with
+   * respect to this execution if `sleep(E') ∩ WI_[E'](v) = empty`, and
+   * `std::nullopt` otherwise
+   */
+  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''`
+   *
+   * The procedure for determining whether `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`
+   *
+   * This method computes the result `v.w'` as needed (viz. only if `v ~_[E] w`
+   * with respect to this execution `E`). The implementation takes advantage
+   * of the fact that determining whether `v ~_[E] w` yields "for free" the
+   * the shortest such `w'` we are looking for; if we ultimately determine
+   * that `v ~_[E] w`, the work we did to do so leaves us precisely with `w'`,
+   * so we can simply prepend `v` to it and call it a day
+   *
+   * @precondition: 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 `v.w'` that should be inserted
+   * as a child of a wakeup tree node representing the sequence `v`
+   * if `v ~_[E] w`, or `std::nullopt` if that relation does not hold
+   * between the two sequences `v` and `w`
+   */
+  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
+   *
+   * @returns the handle to the last event of the execution.
+   * If the sequence is empty, no such handle exists and the
+   * method returns `std::nullopt`
+   */
+  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, each element of which is an
+   * event in this execution which is in a *race* with event `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, each element of which is an event
+   * in this execution which is in a *reversible race* with event `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 but excluding the event `e` itself.
+   * Roughly speaking, the prefix intuitively represents
+   * the "history" of causes which permitted event `e`
+   * to exist
+   */
+  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
+   *
+   * This method has the equivalent effect of pushing the
+   * transitions of the partial execution one-by-one onto
+   * the execution
+   */
+  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..59b96c3
--- /dev/null
@@ -0,0 +1,238 @@
+/* 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 represents a single step
+ * taken in an extension of the execution represented
+ * by the tree within which the node is contained. That is,
+ * a node in the tree is one step on a "pre-defined"
+ * path forward for some execution sequence. The partial
+ * execution that is implicitly represented by the node
+ * is that formed by taking each step on the (unique)
+ * path in the tree from the root node to this node.
+ * Thus, the tree itself contains all of the paths
+ * that "should be" searched, while each node is
+ * simply a step on each path.
+ */
+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 not 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..946e3f4
--- /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;
+struct 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 f1525fe..513c997 100644 (file)
@@ -21,7 +21,7 @@ int main(int argc, char** argv)
   xbt_assert(argc >= 2, "Missing arguments");
 
   // Currently, we need this before sg_config_init:
-  simgrid::mc::model_checking_mode = simgrid::mc::ModelCheckingMode::CHECKER_SIDE;
+  simgrid::mc::set_model_checking_mode(simgrid::mc::ModelCheckingMode::CHECKER_SIDE);
 
   // The initialization function can touch argv.
   // We make a copy of argv before modifying it in order to pass the original value to the model-checked application:
@@ -35,23 +35,24 @@ int main(int argc, char** argv)
 
   std::unique_ptr<Exploration> explo;
 
+#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 (_sg_mc_property_file.get().empty())
-    explo = std::unique_ptr<Exploration>(create_dfs_exploration(argv_copy, cfg_use_DPOR()));
-  else
+  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, get_model_checking_reduction()));
 
+  ExitStatus status;
   try {
     explo->run();
-  } catch (const DeadlockError&) {
-    return SIMGRID_MC_EXIT_DEADLOCK;
-  } catch (const TerminationError&) {
-    return SIMGRID_MC_EXIT_NON_TERMINATION;
-  } catch (const LivenessError&) {
-    return SIMGRID_MC_EXIT_LIVENESS;
+    status = ExitStatus::SUCCESS;
+  } catch (const McError& e) {
+    status = e.value;
   }
-  return SIMGRID_MC_EXIT_SUCCESS;
+  return static_cast<int>(status);
 }
index 98d004b..44ec270 100644 (file)
@@ -9,6 +9,7 @@
 #include "src/mc/explo/udpor/Unfolding.hpp"
 #include "src/mc/explo/udpor/UnfoldingEvent.hpp"
 #include "src/mc/explo/udpor/maximal_subsets_iterator.hpp"
+#include "src/xbt/utils/iter/variable_for_loop.hpp"
 #include "xbt/asserts.h"
 
 #include <algorithm>
@@ -21,20 +22,26 @@ Configuration::Configuration(std::initializer_list<const UnfoldingEvent*> events
 {
 }
 
-Configuration::Configuration(const UnfoldingEvent* e) : Configuration(e->get_history())
+Configuration::Configuration(const UnfoldingEvent* e) : Configuration(e->get_local_config())
 {
   // The local configuration should always be a valid configuration. We
   // check the invariant regardless as a sanity check
 }
 
+Configuration::Configuration(const History& history) : Configuration(history.get_all_events()) {}
+
 Configuration::Configuration(const EventSet& events) : events_(events)
 {
   if (!events_.is_valid_configuration()) {
     throw std::invalid_argument("The events do not form a valid configuration");
   }
-}
 
-Configuration::Configuration(const History& history) : Configuration(history.get_all_events()) {}
+  // Since we add in topological order under `<`, we know that the "most-recent"
+  // transition executed by each actor will appear last
+  for (const UnfoldingEvent* e : get_topologically_sorted_events()) {
+    this->latest_event_mapping[e->get_actor()] = e;
+  }
+}
 
 void Configuration::add_event(const UnfoldingEvent* e)
 {
@@ -42,19 +49,22 @@ void Configuration::add_event(const UnfoldingEvent* e)
     throw std::invalid_argument("Expected a nonnull `UnfoldingEvent*` but received NULL instead");
   }
 
+  // The event is already a member of the configuration: there's
+  // nothing to do in this case
   if (this->events_.contains(e)) {
     return;
   }
 
   // Preserves the property that the configuration is conflict-free
-  if (e->conflicts_with(*this)) {
+  if (e->conflicts_with_any(this->events_)) {
     throw std::invalid_argument("The newly added event conflicts with the events already "
                                 "contained in the configuration. Adding this event violates "
                                 "the property that a configuration is conflict-free");
   }
 
   this->events_.insert(e);
-  this->newest_event = e;
+  this->newest_event                         = e;
+  this->latest_event_mapping[e->get_actor()] = e;
 
   // Preserves the property that the configuration is causally closed
   if (auto history = History(e); !this->events_.contains(history)) {
@@ -65,13 +75,41 @@ void Configuration::add_event(const UnfoldingEvent* e)
 
 bool Configuration::is_compatible_with(const UnfoldingEvent* e) const
 {
-  return not e->conflicts_with(*this);
+  // 1. `e`'s history must be contained in the configuration;
+  // otherwise adding the event would violate the invariant
+  // that a configuration is causally-closed
+  //
+  // 2. `e` itself must not conflict with any events of
+  // the configuration; otherwise adding the event would
+  // violate the invariant that a configuration is conflict-free
+  return contains(e->get_history()) and (not e->conflicts_with_any(this->events_));
 }
 
 bool Configuration::is_compatible_with(const History& history) const
 {
-  return std::none_of(history.begin(), history.end(),
-                      [&](const UnfoldingEvent* e) { return e->conflicts_with(*this); });
+  // Note: We don't need to check if the `C` will be causally-closed
+  // after adding `history` to it since a) `C` itself is already
+  // causally-closed and b) the history is already causally closed
+  const auto event_diff = history.get_event_diff_with(*this);
+
+  // The events that are contained outside of the configuration
+  // must themselves be free of conflicts.
+  if (not event_diff.is_conflict_free()) {
+    return false;
+  }
+
+  // Now we need only ensure that there are no conflicts
+  // between events of the configuration and the events
+  // that lie outside of the configuration. There is no
+  // need to check if there are conflicts in `C`: we already
+  // know that it's conflict free
+  const auto begin = simgrid::xbt::variable_for_loop<const EventSet>{{event_diff}, {this->events_}};
+  const auto end   = simgrid::xbt::variable_for_loop<const EventSet>();
+  return std::none_of(begin, end, [=](const auto event_pair) {
+    const UnfoldingEvent* e1 = *event_pair[0];
+    const UnfoldingEvent* e2 = *event_pair[1];
+    return e1->conflicts_with(e2);
+  });
 }
 
 std::vector<const UnfoldingEvent*> Configuration::get_topologically_sorted_events() const
@@ -125,9 +163,9 @@ std::optional<Configuration> Configuration::compute_k_partial_alternative_to(con
                                                                              size_t k) const
 {
   // 1. Select k (of |D|, whichever is smaller) arbitrary events e_1, ..., e_k from D
-  const auto D_hat = [&]() {
-    const size_t size = std::min(k, D.size());
-    std::vector<const UnfoldingEvent*> D_hat(size);
+  const size_t k_alt_size = std::min(k, D.size());
+  const auto D_hat        = [&k_alt_size, &D]() {
+    std::vector<const UnfoldingEvent*> D_hat(k_alt_size);
     // TODO: Since any subset suffices for computing `k`-partial alternatives,
     // potentially select intelligently here (e.g. perhaps pick events
     // with transitions that we know are totally independent). This may be
@@ -135,7 +173,7 @@ std::optional<Configuration> Configuration::compute_k_partial_alternative_to(con
     // UDPOR
     //
     // For now, simply pick the first `k` events
-    std::copy_n(D.begin(), size, D_hat.begin());
+    std::copy_n(D.begin(), k_alt_size, D_hat.begin());
     return D_hat;
   }();
 
@@ -153,7 +191,7 @@ std::optional<Configuration> Configuration::compute_k_partial_alternative_to(con
   Comb comb(k);
 
   for (const auto* e : U) {
-    for (unsigned i = 0; i < k; i++) {
+    for (size_t i = 0; i < k_alt_size; i++) {
       const UnfoldingEvent* e_i = D_hat[i];
       if (const auto e_local_config = History(e);
           e_i->conflicts_with(e) and (not D.intersects(e_local_config)) and is_compatible_with(e_local_config)) {
@@ -190,4 +228,20 @@ std::optional<Configuration> Configuration::compute_k_partial_alternative_to(con
   return Configuration(History(map_events(*alternative)));
 }
 
+std::optional<const UnfoldingEvent*> Configuration::get_latest_event_of(aid_t aid) const
+{
+  if (const auto latest_event = latest_event_mapping.find(aid); latest_event != latest_event_mapping.end()) {
+    return std::optional<const UnfoldingEvent*>{latest_event->second};
+  }
+  return std::nullopt;
+}
+
+std::optional<const Transition*> Configuration::get_latest_action_of(aid_t aid) const
+{
+  if (const auto latest_event = get_latest_event_of(aid); latest_event.has_value()) {
+    return std::optional<const Transition*>{latest_event.value()->get_transition()};
+  }
+  return std::nullopt;
+}
+
 } // namespace simgrid::mc::udpor
index 619871e..b1f6f93 100644 (file)
@@ -10,6 +10,8 @@
 #include "src/mc/explo/udpor/udpor_forward.hpp"
 
 #include <optional>
+#include <string>
+#include <unordered_map>
 
 namespace simgrid::mc::udpor {
 
@@ -31,8 +33,10 @@ public:
   auto cend() const { return this->events_.cend(); }
 
   bool contains(const UnfoldingEvent* e) const { return this->events_.contains(e); }
+  bool contains(const EventSet& events) const { return events.is_subset_of(this->events_); }
   const EventSet& get_events() const { return this->events_; }
   const UnfoldingEvent* get_latest_event() const { return this->newest_event; }
+  std::string to_string() const { return this->events_.to_string(); }
 
   /**
    * @brief Insert a new event into the configuration
@@ -65,8 +69,16 @@ public:
 
   /**
    * @brief Whether or not the given event can be added to
-   * this configuration while keeping the set of events causally closed
-   * and conflict-free
+   * this configuration while preserving that the configuration
+   * is causally closed and conflict-free
+   *
+   * A configuration `C` is compatible with an event iff
+   * the event can be added to `C` while preserving that
+   * the configuration is causally closed and conflict-free.
+   *
+   * The method effectively answers the following question:
+   *
+   * "Is `C + {e}` a valid configuration?"
    */
   bool is_compatible_with(const UnfoldingEvent* e) const;
 
@@ -74,6 +86,14 @@ public:
    * @brief Whether or not the events in the given history can be added to
    * this configuration while keeping the set of events causally closed
    * and conflict-free
+   *
+   * A configuration `C` is compatible with a history iff all
+   * events of the history can be added to `C` while preserving
+   * that the configuration is causally closed and conflict-free.
+   *
+   * The method effectively answers the following question:
+   *
+   * "Is `C + (U_i [e_i])` a valid configuration?"
    */
   bool is_compatible_with(const History& history) const;
 
@@ -134,6 +154,40 @@ public:
    */
   EventSet get_minimally_reproducible_events() const;
 
+  /**
+   * @brief Determines the event in the configuration whose associated
+   * transition is the latest of the given actor
+   *
+   * @invariant: At most one event in the configuration will correspond
+   * to `preEvt(C, a)` for any action `a`. This can be argued by contradiction.
+   *
+   * If there were more than one event (`e` and `e'`) in any configuration whose
+   * associated transitions `a` were run by the same actor at the same step, then they
+   * could not be causally related (`<`) since `a` could not be enabled in
+   * both subconfigurations C' and C'' containing the hypothetical events
+   * `e` and `e` + `e'`. Since they would not be contained in each other's histories,
+   * they would be in conflict, which cannot happen between any pair of events
+   * in a configuration. Thus `e` and `e'` cannot exist simultaneously
+   */
+  std::optional<const UnfoldingEvent*> get_latest_event_of(aid_t) const;
+  /**
+   * @brief Determines the most recent transition of the given actor
+   * in this configuration, or `pre(a)` as denoted in the thesis of
+   * The Anh Pham
+   *
+   * Conceptually, the execution of an interleaving of the transitions
+   * (i.e. a topological ordering) of a configuration yields a unique
+   * state `state(C)`. Since actions taken by the same actor are always
+   * dependent with one another, any such interleaving always yields a
+   * unique
+   *
+   * @returns the most recent transition of the given actor
+   * in this configuration, or `std::nullopt` if there are no transitions
+   * in this configuration run by the given actor
+   */
+  std::optional<const Transition*> get_latest_action_of(aid_t aid) const;
+  std::optional<const UnfoldingEvent*> pre_event(aid_t aid) const { return get_latest_event_of(aid); }
+
 private:
   /**
    * @brief The most recent event added to the configuration
@@ -145,8 +199,20 @@ private:
    *
    * @invariant For each event `e` in `events_`, the set of
    * dependencies of `e` is also contained in `events_`
+   *
+   * @invariant For each pair of events `e` and `e'` in
+   * `events_`, `e` and `e'` are not in conflict
    */
   EventSet events_;
+
+  /**
+   * @brief Maps actors to the latest events which
+   * are executed by the actor
+   *
+   * @invariant: The events that are contained in the map
+   * are also contained in the set `events_`
+   */
+  std::unordered_map<aid_t, const UnfoldingEvent*> latest_event_mapping;
 };
 
 } // namespace simgrid::mc::udpor
index 8deb62e..4e33378 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <unordered_map>
 
+using namespace simgrid::mc;
 using namespace simgrid::mc::udpor;
 
 TEST_CASE("simgrid::mc::udpor::Configuration: Constructing Configurations")
@@ -26,11 +27,11 @@ TEST_CASE("simgrid::mc::udpor::Configuration: Constructing Configurations")
   //          e3
   //         /  /
   //        e4   e5
-  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>());
-  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e5(EventSet({&e3}), std::make_shared<IndependentAction>());
+  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>(0));
+  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>(1));
+  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(2));
+  UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>(3));
+  UnfoldingEvent e5(EventSet({&e3}), std::make_shared<IndependentAction>(4));
 
   SECTION("Creating a configuration without events")
   {
@@ -95,10 +96,10 @@ TEST_CASE("simgrid::mc::udpor::Configuration: Adding Events")
   //           /
   //         /  /
   //        e3   e4
-  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>());
-  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e4(EventSet({&e2}), std::make_shared<IndependentAction>());
+  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>(0));
+  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>(1));
+  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(2));
+  UnfoldingEvent e4(EventSet({&e2}), std::make_shared<IndependentAction>(3));
 
   REQUIRE_THROWS_AS(Configuration().add_event(nullptr), std::invalid_argument);
   REQUIRE_THROWS_AS(Configuration().add_event(&e2), std::invalid_argument);
@@ -138,10 +139,10 @@ TEST_CASE("simgrid::mc::udpor::Configuration: Topological Sort Order")
   //          e3
   //         /
   //        e4
-  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>());
-  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>());
+  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>(1));
+  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>(2));
+  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(3));
+  UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>(4));
 
   SECTION("Topological ordering for entire set")
   {
@@ -196,12 +197,12 @@ TEST_CASE("simgrid::mc::udpor::Configuration: Topological Sort Order More Compli
   //        e4   e6
   //        /
   //       e5
-  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>());
-  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e5(EventSet({&e4}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e6(EventSet({&e3}), std::make_shared<IndependentAction>());
+  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>(1));
+  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>(2));
+  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(3));
+  UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>(4));
+  UnfoldingEvent e5(EventSet({&e4}), std::make_shared<IndependentAction>(5));
+  UnfoldingEvent e6(EventSet({&e3}), std::make_shared<IndependentAction>(6));
 
   SECTION("Topological ordering for subsets")
   {
@@ -305,18 +306,18 @@ TEST_CASE("simgrid::mc::udpor::Configuration: Topological Sort Order Very Compli
   //        /   /     /
   //         /  /   /
   //         [   e12    ]
-  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>());
-  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e8(EventSet({&e1}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e5(EventSet({&e4}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e6(EventSet({&e4}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e7(EventSet({&e2, &e8}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e9(EventSet({&e6, &e7}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e10(EventSet({&e7}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e11(EventSet({&e8}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e12(EventSet({&e5, &e9, &e10}), std::make_shared<IndependentAction>());
+  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>(1));
+  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>(2));
+  UnfoldingEvent e8(EventSet({&e1}), std::make_shared<IndependentAction>(3));
+  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(4));
+  UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>(5));
+  UnfoldingEvent e5(EventSet({&e4}), std::make_shared<IndependentAction>(6));
+  UnfoldingEvent e6(EventSet({&e4}), std::make_shared<IndependentAction>(7));
+  UnfoldingEvent e7(EventSet({&e2, &e8}), std::make_shared<IndependentAction>(8));
+  UnfoldingEvent e9(EventSet({&e6, &e7}), std::make_shared<IndependentAction>(9));
+  UnfoldingEvent e10(EventSet({&e7}), std::make_shared<IndependentAction>(10));
+  UnfoldingEvent e11(EventSet({&e8}), std::make_shared<IndependentAction>(11));
+  UnfoldingEvent e12(EventSet({&e5, &e9, &e10}), std::make_shared<IndependentAction>(12));
   Configuration C{&e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12};
 
   SECTION("Test every combination of the maximal configuration (forward graph)")
@@ -407,14 +408,14 @@ TEST_CASE("simgrid::mc::udpor::maximal_subsets_iterator: Basic Testing of Maxima
   //           e3    e6
   //           /     / /
   //          e4    e7 e8
-  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>());
-  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e5(EventSet({&e1}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e6(EventSet({&e5}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e7(EventSet({&e6}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e8(EventSet({&e6}), std::make_shared<IndependentAction>());
+  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>(0));
+  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>(1));
+  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(2));
+  UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>(3));
+  UnfoldingEvent e5(EventSet({&e1}), std::make_shared<IndependentAction>(4));
+  UnfoldingEvent e6(EventSet({&e5}), std::make_shared<IndependentAction>(5));
+  UnfoldingEvent e7(EventSet({&e6}), std::make_shared<IndependentAction>(6));
+  UnfoldingEvent e8(EventSet({&e6}), std::make_shared<IndependentAction>(7));
 
   SECTION("Iteration over an empty configuration yields only the empty set")
   {
@@ -465,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) {
@@ -498,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) {
@@ -540,24 +543,24 @@ TEST_CASE("simgrid::mc::udpor::maximal_subsets_iterator: Stress Test for Maximal
   //               |   e11 e12 e13 e14   e15
   //               |   /      / / /   /  /
   //               +-> e16     e17     e18
-  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>());
-  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e3(EventSet({&e1}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e4(EventSet({&e2}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e5(EventSet({&e2}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e6(EventSet({&e3}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e7(EventSet({&e3}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e8(EventSet({&e4}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e9(EventSet({&e4, &e5, &e6}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e10(EventSet({&e6, &e7}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e11(EventSet({&e8}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e12(EventSet({&e8}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e13(EventSet({&e9}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e14(EventSet({&e9}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e15(EventSet({&e10}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e16(EventSet({&e5, &e11}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e17(EventSet({&e12, &e13, &e14}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e18(EventSet({&e14, &e15}), std::make_shared<IndependentAction>());
+  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>(1));
+  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>(2));
+  UnfoldingEvent e3(EventSet({&e1}), std::make_shared<IndependentAction>(3));
+  UnfoldingEvent e4(EventSet({&e2}), std::make_shared<IndependentAction>(4));
+  UnfoldingEvent e5(EventSet({&e2}), std::make_shared<IndependentAction>(5));
+  UnfoldingEvent e6(EventSet({&e3}), std::make_shared<IndependentAction>(6));
+  UnfoldingEvent e7(EventSet({&e3}), std::make_shared<IndependentAction>(7));
+  UnfoldingEvent e8(EventSet({&e4}), std::make_shared<IndependentAction>(8));
+  UnfoldingEvent e9(EventSet({&e4, &e5, &e6}), std::make_shared<IndependentAction>(9));
+  UnfoldingEvent e10(EventSet({&e6, &e7}), std::make_shared<IndependentAction>(10));
+  UnfoldingEvent e11(EventSet({&e8}), std::make_shared<IndependentAction>(11));
+  UnfoldingEvent e12(EventSet({&e8}), std::make_shared<IndependentAction>(12));
+  UnfoldingEvent e13(EventSet({&e9}), std::make_shared<IndependentAction>(13));
+  UnfoldingEvent e14(EventSet({&e9}), std::make_shared<IndependentAction>(14));
+  UnfoldingEvent e15(EventSet({&e10}), std::make_shared<IndependentAction>(15));
+  UnfoldingEvent e16(EventSet({&e5, &e11}), std::make_shared<IndependentAction>(16));
+  UnfoldingEvent e17(EventSet({&e12, &e13, &e14}), std::make_shared<IndependentAction>(17));
+  UnfoldingEvent e18(EventSet({&e14, &e15}), std::make_shared<IndependentAction>(18));
   Configuration C{&e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18};
 
   SECTION("Every subset iterated over is maximal")
@@ -601,7 +604,196 @@ TEST_CASE("simgrid::mc::udpor::maximal_subsets_iterator: Stress Test for Maximal
   }
 }
 
-TEST_CASE("simgrid::mc::udpor:Configuration: Computing Full Alternatives in Reader/Writer Example")
+TEST_CASE("simgrid::mc::udpor::Configuration: Latest Transitions")
+{
+  // The following tests concern the given event structure (labeled as "event(actor)")
+  //                  e1(1)
+  //                 /     /
+  //              e2(1)   e3(2)
+  //              /    //     /
+  //            e4(3) e5(2)  e6(1)
+  //                  /   /
+  //               e7(1) e8(1)
+  const auto t1 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
+  const auto t2 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
+  const auto t3 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+  const auto t4 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 3);
+  const auto t5 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 2);
+  const auto t6 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
+  const auto t7 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
+  const auto t8 = std::make_shared<IndependentAction>(Transition::Type::UNKNOWN, 1);
+
+  const UnfoldingEvent e1(EventSet(), t1);
+  const UnfoldingEvent e2(EventSet({&e1}), t2);
+  const UnfoldingEvent e3(EventSet({&e1}), t3);
+  const UnfoldingEvent e4(EventSet({&e2}), t4);
+  const UnfoldingEvent e5(EventSet({&e2, &e3}), t5);
+  const UnfoldingEvent e6(EventSet({&e3}), t6);
+  const UnfoldingEvent e7(EventSet({&e5}), t7);
+  const UnfoldingEvent e8(EventSet({&e5}), t8);
+
+  SECTION("Test that the latest events are correct on initialization")
+  {
+    SECTION("Empty configuration has no events")
+    {
+      Configuration C;
+      REQUIRE_FALSE(C.get_latest_event_of(1).has_value());
+      REQUIRE_FALSE(C.get_latest_event_of(2).has_value());
+      REQUIRE_FALSE(C.get_latest_event_of(3).has_value());
+
+      REQUIRE_FALSE(C.get_latest_action_of(1).has_value());
+      REQUIRE_FALSE(C.get_latest_action_of(2).has_value());
+      REQUIRE_FALSE(C.get_latest_action_of(3).has_value());
+    }
+
+    SECTION("Missing two actors")
+    {
+      Configuration C{&e1};
+      REQUIRE(C.get_latest_event_of(1).has_value());
+      REQUIRE(C.get_latest_event_of(1).value() == &e1);
+
+      REQUIRE_FALSE(C.get_latest_event_of(2).has_value());
+      REQUIRE_FALSE(C.get_latest_event_of(3).has_value());
+
+      REQUIRE(C.get_latest_action_of(1).has_value());
+      REQUIRE(C.get_latest_action_of(1).value() == t1.get());
+
+      REQUIRE_FALSE(C.get_latest_action_of(2).has_value());
+      REQUIRE_FALSE(C.get_latest_action_of(3).has_value());
+    }
+
+    SECTION("Two events with one actor yields the latest event")
+    {
+      Configuration C{&e1, &e2};
+      REQUIRE(C.get_latest_event_of(1).has_value());
+      REQUIRE(C.get_latest_event_of(1).value() == &e2);
+
+      REQUIRE_FALSE(C.get_latest_event_of(2).has_value());
+      REQUIRE_FALSE(C.get_latest_event_of(3).has_value());
+
+      REQUIRE(C.get_latest_action_of(1).has_value());
+      REQUIRE(C.get_latest_action_of(1).value() == t2.get());
+
+      REQUIRE_FALSE(C.get_latest_action_of(2).has_value());
+      REQUIRE_FALSE(C.get_latest_action_of(3).has_value());
+    }
+
+    SECTION("Two events with two actors")
+    {
+      Configuration C{&e1, &e3};
+      REQUIRE(C.get_latest_event_of(1).has_value());
+      REQUIRE(C.get_latest_event_of(1).value() == &e1);
+
+      REQUIRE(C.get_latest_event_of(2).has_value());
+      REQUIRE(C.get_latest_event_of(2).value() == &e3);
+
+      REQUIRE_FALSE(C.get_latest_event_of(3).has_value());
+
+      REQUIRE(C.get_latest_action_of(1).has_value());
+      REQUIRE(C.get_latest_action_of(1).value() == t1.get());
+
+      REQUIRE(C.get_latest_action_of(2).has_value());
+      REQUIRE(C.get_latest_action_of(2).value() == t3.get());
+
+      REQUIRE_FALSE(C.get_latest_action_of(3).has_value());
+    }
+
+    SECTION("Three different actors actors")
+    {
+      Configuration C{&e1, &e2, &e3, &e4, &e5};
+      REQUIRE(C.get_latest_event_of(1).has_value());
+      REQUIRE(C.get_latest_event_of(1).value() == &e2);
+
+      REQUIRE(C.get_latest_event_of(2).has_value());
+      REQUIRE(C.get_latest_event_of(2).value() == &e5);
+
+      REQUIRE(C.get_latest_event_of(3).has_value());
+      REQUIRE(C.get_latest_event_of(3).value() == &e4);
+
+      REQUIRE(C.get_latest_action_of(1).has_value());
+      REQUIRE(C.get_latest_action_of(1).value() == t2.get());
+
+      REQUIRE(C.get_latest_action_of(2).has_value());
+      REQUIRE(C.get_latest_action_of(2).value() == t5.get());
+
+      REQUIRE(C.get_latest_action_of(3).has_value());
+      REQUIRE(C.get_latest_action_of(3).value() == t4.get());
+    }
+  }
+
+  SECTION("Test that the latest events are correct when adding new events")
+  {
+    Configuration C;
+    REQUIRE_FALSE(C.get_latest_event_of(1).has_value());
+    REQUIRE_FALSE(C.get_latest_event_of(2).has_value());
+    REQUIRE_FALSE(C.get_latest_event_of(3).has_value());
+    REQUIRE_FALSE(C.get_latest_action_of(1).has_value());
+    REQUIRE_FALSE(C.get_latest_action_of(2).has_value());
+    REQUIRE_FALSE(C.get_latest_action_of(3).has_value());
+
+    C.add_event(&e1);
+    REQUIRE(C.get_latest_event_of(1).has_value());
+    REQUIRE(C.get_latest_event_of(1).value() == &e1);
+    REQUIRE_FALSE(C.get_latest_event_of(2).has_value());
+    REQUIRE_FALSE(C.get_latest_event_of(3).has_value());
+    REQUIRE(C.get_latest_action_of(1).has_value());
+    REQUIRE(C.get_latest_action_of(1).value() == t1.get());
+    REQUIRE_FALSE(C.get_latest_action_of(2).has_value());
+    REQUIRE_FALSE(C.get_latest_action_of(3).has_value());
+
+    C.add_event(&e2);
+    REQUIRE(C.get_latest_event_of(1).has_value());
+    REQUIRE(C.get_latest_event_of(1).value() == &e2);
+    REQUIRE_FALSE(C.get_latest_event_of(2).has_value());
+    REQUIRE_FALSE(C.get_latest_event_of(3).has_value());
+    REQUIRE(C.get_latest_action_of(1).has_value());
+    REQUIRE(C.get_latest_action_of(1).value() == t2.get());
+    REQUIRE_FALSE(C.get_latest_action_of(2).has_value());
+    REQUIRE_FALSE(C.get_latest_action_of(3).has_value());
+
+    C.add_event(&e3);
+    REQUIRE(C.get_latest_event_of(1).has_value());
+    REQUIRE(C.get_latest_event_of(1).value() == &e2);
+    REQUIRE(C.get_latest_event_of(2).has_value());
+    REQUIRE(C.get_latest_event_of(2).value() == &e3);
+    REQUIRE_FALSE(C.get_latest_event_of(3).has_value());
+    REQUIRE(C.get_latest_action_of(1).has_value());
+    REQUIRE(C.get_latest_action_of(1).value() == t2.get());
+    REQUIRE(C.get_latest_action_of(2).has_value());
+    REQUIRE(C.get_latest_action_of(2).value() == t3.get());
+    REQUIRE_FALSE(C.get_latest_action_of(3).has_value());
+
+    C.add_event(&e4);
+    REQUIRE(C.get_latest_event_of(1).has_value());
+    REQUIRE(C.get_latest_event_of(1).value() == &e2);
+    REQUIRE(C.get_latest_event_of(2).has_value());
+    REQUIRE(C.get_latest_event_of(2).value() == &e3);
+    REQUIRE(C.get_latest_event_of(3).has_value());
+    REQUIRE(C.get_latest_event_of(3).value() == &e4);
+    REQUIRE(C.get_latest_action_of(1).has_value());
+    REQUIRE(C.get_latest_action_of(1).value() == t2.get());
+    REQUIRE(C.get_latest_action_of(2).has_value());
+    REQUIRE(C.get_latest_action_of(2).value() == t3.get());
+    REQUIRE(C.get_latest_action_of(3).has_value());
+    REQUIRE(C.get_latest_action_of(3).value() == t4.get());
+
+    C.add_event(&e5);
+    REQUIRE(C.get_latest_event_of(1).has_value());
+    REQUIRE(C.get_latest_event_of(1).value() == &e2);
+    REQUIRE(C.get_latest_event_of(2).has_value());
+    REQUIRE(C.get_latest_event_of(2).value() == &e5);
+    REQUIRE(C.get_latest_event_of(3).has_value());
+    REQUIRE(C.get_latest_event_of(3).value() == &e4);
+    REQUIRE(C.get_latest_action_of(1).has_value());
+    REQUIRE(C.get_latest_action_of(1).value() == t2.get());
+    REQUIRE(C.get_latest_action_of(2).has_value());
+    REQUIRE(C.get_latest_action_of(2).value() == t5.get());
+    REQUIRE(C.get_latest_action_of(3).has_value());
+    REQUIRE(C.get_latest_action_of(3).value() == t4.get());
+  }
+}
+
+TEST_CASE("simgrid::mc::udpor::Configuration: Computing Full Alternatives in Reader/Writer Example")
 {
   // The following tests concern the given event structure that is given as
   // an example in figure 1 of the original UDPOR paper.
@@ -620,37 +812,48 @@ TEST_CASE("simgrid::mc::udpor:Configuration: Computing Full Alternatives in Read
   // then `e4`, and then `e7`
   Unfolding U;
 
-  auto e0        = std::make_unique<UnfoldingEvent>(EventSet(), std::make_shared<ConditionallyDependentAction>());
+  auto e0 = std::make_unique<UnfoldingEvent>(
+      EventSet(), std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 0));
   auto e0_handle = e0.get();
 
-  auto e1        = std::make_unique<UnfoldingEvent>(EventSet({e0_handle}), std::make_shared<DependentAction>());
+  auto e1        = std::make_unique<UnfoldingEvent>(EventSet({e0_handle}),
+                                             std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 0));
   auto e1_handle = e1.get();
 
-  auto e2 = std::make_unique<UnfoldingEvent>(EventSet({e1_handle}), std::make_shared<ConditionallyDependentAction>());
+  auto e2 = std::make_unique<UnfoldingEvent>(
+      EventSet({e1_handle}), std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1));
   auto e2_handle = e2.get();
 
-  auto e3 = std::make_unique<UnfoldingEvent>(EventSet({e1_handle}), std::make_shared<ConditionallyDependentAction>());
+  auto e3 = std::make_unique<UnfoldingEvent>(
+      EventSet({e1_handle}), std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2));
   auto e3_handle = e3.get();
 
-  auto e4 = std::make_unique<UnfoldingEvent>(EventSet({e0_handle}), std::make_shared<ConditionallyDependentAction>());
+  auto e4 = std::make_unique<UnfoldingEvent>(
+      EventSet({e0_handle}), std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1));
   auto e4_handle = e4.get();
 
-  auto e5        = std::make_unique<UnfoldingEvent>(EventSet({e4_handle}), std::make_shared<DependentAction>());
+  auto e5        = std::make_unique<UnfoldingEvent>(EventSet({e4_handle}),
+                                             std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 0));
   auto e5_handle = e5.get();
 
-  auto e6 = std::make_unique<UnfoldingEvent>(EventSet({e5_handle}), std::make_shared<ConditionallyDependentAction>());
+  auto e6 = std::make_unique<UnfoldingEvent>(
+      EventSet({e5_handle}), std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2));
   auto e6_handle = e6.get();
 
-  auto e7 = std::make_unique<UnfoldingEvent>(EventSet({e0_handle}), std::make_shared<ConditionallyDependentAction>());
+  auto e7 = std::make_unique<UnfoldingEvent>(
+      EventSet({e0_handle}), std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 2));
   auto e7_handle = e7.get();
 
-  auto e8 = std::make_unique<UnfoldingEvent>(EventSet({e4_handle, e7_handle}), std::make_shared<DependentAction>());
+  auto e8        = std::make_unique<UnfoldingEvent>(EventSet({e4_handle, e7_handle}),
+                                             std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 0));
   auto e8_handle = e8.get();
 
-  auto e9        = std::make_unique<UnfoldingEvent>(EventSet({e7_handle}), std::make_shared<DependentAction>());
+  auto e9        = std::make_unique<UnfoldingEvent>(EventSet({e7_handle}),
+                                             std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 0));
   auto e9_handle = e9.get();
 
-  auto e10 = std::make_unique<UnfoldingEvent>(EventSet({e9_handle}), std::make_shared<ConditionallyDependentAction>());
+  auto e10 = std::make_unique<UnfoldingEvent>(
+      EventSet({e9_handle}), std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 1));
   auto e10_handle = e10.get();
 
   SECTION("Alternative computation call 1")
@@ -1163,4 +1366,4 @@ TEST_CASE("simgrid::mc::udpor:Configuration: Computing Full Alternatives in Read
       REQUIRE(alternative.value().get_events() == EventSet({e0_handle, e7_handle, e9_handle}));
     }
   }
-}
\ No newline at end of file
+}
index 09cb66b..b644448 100644 (file)
@@ -142,13 +142,23 @@ bool EventSet::intersects(const History& history) const
   return std::any_of(history.begin(), history.end(), [=](const UnfoldingEvent* e) { return this->contains(e); });
 }
 
+bool EventSet::intersects(const EventSet& other) const
+{
+  return std::any_of(other.begin(), other.end(), [=](const UnfoldingEvent* e) { return this->contains(e); });
+}
+
+EventSet EventSet::get_largest_maximal_subset() const
+{
+  const History history(*this);
+  return history.get_all_maximal_events();
+}
+
 bool EventSet::is_maximal() const
 {
   // A set of events is maximal if no event from
   // the original set is ruled out when traversing
   // the history of the events
-  const History history(*this);
-  return *this == history.get_all_maximal_events();
+  return *this == this->get_largest_maximal_subset();
 }
 
 bool EventSet::is_conflict_free() const
@@ -233,6 +243,18 @@ std::vector<const UnfoldingEvent*> EventSet::get_topological_ordering_of_reverse
   return topological_events;
 }
 
+std::string EventSet::to_string() const
+{
+  std::string contents;
+
+  for (const auto* event : *this) {
+    contents += event->to_string();
+    contents += " + ";
+  }
+
+  return contents;
+}
+
 std::vector<const UnfoldingEvent*> EventSet::move_into_vector() const&&
 {
   std::vector<const UnfoldingEvent*> contents;
index 54c00c8..ddd875d 100644 (file)
@@ -8,10 +8,12 @@
 
 #include "src/mc/explo/udpor/udpor_forward.hpp"
 
+#include <algorithm>
 #include <cstddef>
 #include <initializer_list>
 #include <unordered_set>
 #include <vector>
+#include <xbt/asserts.h>
 
 namespace simgrid::mc::udpor {
 
@@ -26,9 +28,18 @@ public:
   EventSet& operator=(EventSet&&)      = default;
   EventSet(EventSet&&)                 = default;
   explicit EventSet(Configuration&& config);
-  explicit EventSet(std::vector<const UnfoldingEvent*>&& raw_events) : events_(raw_events.begin(), raw_events.end()) {}
-  explicit EventSet(std::unordered_set<const UnfoldingEvent*>&& raw_events) : events_(raw_events) {}
-  explicit EventSet(std::initializer_list<const UnfoldingEvent*> event_list) : events_(std::move(event_list)) {}
+  explicit EventSet(std::vector<const UnfoldingEvent*>&& raw_events) : events_(raw_events.begin(), raw_events.end())
+  {
+    xbt_assert(std::none_of(events_.begin(), events_.end(), [](const auto* e) { return e == nullptr; }), "Oh no");
+  }
+  explicit EventSet(std::unordered_set<const UnfoldingEvent*>&& raw_events) : events_(raw_events)
+  {
+    xbt_assert(std::none_of(events_.begin(), events_.end(), [](const auto* e) { return e == nullptr; }), "Oh no");
+  }
+  explicit EventSet(std::initializer_list<const UnfoldingEvent*> event_list) : events_(std::move(event_list))
+  {
+    xbt_assert(std::none_of(events_.begin(), events_.end(), [](const auto* e) { return e == nullptr; }), "Oh no");
+  }
 
   auto begin() const { return this->events_.begin(); }
   auto end() const { return this->events_.end(); }
@@ -54,11 +65,13 @@ public:
   bool empty() const;
   bool contains(const UnfoldingEvent*) const;
   bool contains(const History&) const;
+  bool intersects(const EventSet&) const;
   bool intersects(const History&) const;
   bool is_subset_of(const EventSet&) const;
 
   bool operator==(const EventSet& other) const { return this->events_ == other.events_; }
   bool operator!=(const EventSet& other) const { return this->events_ != other.events_; }
+  std::string to_string() const;
 
   /**
    * @brief Whether or not this set of events could
@@ -104,6 +117,12 @@ public:
    */
   bool is_conflict_free() const;
 
+  /**
+   * @brief Produces the largest subset of this
+   * set of events which is maximal
+   */
+  EventSet get_largest_maximal_subset() const;
+
   /**
    * @brief Orders the events of the set such that
    * "more recent" events (i.e. those that are farther down in
index 684ac5d..3fc7349 100644 (file)
@@ -41,7 +41,9 @@ TEST_CASE("simgrid::mc::udpor::EventSet: Initial conditions when creating sets")
 
   SECTION("Initialization with one or more elements")
   {
-    UnfoldingEvent e1, e2, e3;
+    UnfoldingEvent e1;
+    UnfoldingEvent e2;
+    UnfoldingEvent e3;
 
     SECTION("Set initializer")
     {
@@ -68,7 +70,9 @@ TEST_CASE("simgrid::mc::udpor::EventSet: Initial conditions when creating sets")
 TEST_CASE("simgrid::mc::udpor::EventSet: Insertions")
 {
   EventSet event_set;
-  UnfoldingEvent e1, e2, e3;
+  UnfoldingEvent e1;
+  UnfoldingEvent e2;
+  UnfoldingEvent e3;
 
   SECTION("Inserting unique elements")
   {
@@ -192,7 +196,9 @@ TEST_CASE("simgrid::mc::udpor::EventSet: Set Equality")
   UnfoldingEvent e2;
   UnfoldingEvent e3;
   UnfoldingEvent e4;
-  EventSet A{&e1, &e2, &e3}, B{&e1, &e2, &e3}, C{&e1, &e2, &e3};
+  EventSet A{&e1, &e2, &e3};
+  EventSet B{&e1, &e2, &e3};
+  EventSet C{&e1, &e2, &e3};
 
   SECTION("Equality implies containment")
   {
@@ -267,10 +273,16 @@ TEST_CASE("simgrid::mc::udpor::EventSet: Set Equality")
 
 TEST_CASE("simgrid::mc::udpor::EventSet: Set Union Tests")
 {
-  UnfoldingEvent e1, e2, e3, e4;
+  UnfoldingEvent e1;
+  UnfoldingEvent e2;
+  UnfoldingEvent e3;
+  UnfoldingEvent e4;
 
   // C = A + B
-  EventSet A{&e1, &e2, &e3}, B{&e2, &e3, &e4}, C{&e1, &e2, &e3, &e4}, D{&e1, &e3};
+  EventSet A{&e1, &e2, &e3};
+  EventSet B{&e2, &e3, &e4};
+  EventSet C{&e1, &e2, &e3, &e4};
+  EventSet D{&e1, &e3};
 
   SECTION("Unions with no effect")
   {
@@ -395,7 +407,12 @@ TEST_CASE("simgrid::mc::udpor::EventSet: Set Difference Tests")
   // D is a subset of A and C
   // E is a subset of B and C
   // F is a subset of A, C, and D
-  EventSet A{&e1, &e2, &e3}, B{&e2, &e3, &e4}, C{&e1, &e2, &e3, &e4}, D{&e1, &e3}, E{&e4}, F{&e1};
+  EventSet A{&e1, &e2, &e3};
+  EventSet B{&e2, &e3, &e4};
+  EventSet C{&e1, &e2, &e3, &e4};
+  EventSet D{&e1, &e3};
+  EventSet E{&e4};
+  EventSet F{&e1};
 
   SECTION("Difference with no effect")
   {
@@ -465,7 +482,10 @@ TEST_CASE("simgrid::mc::udpor::EventSet: Set Difference Tests")
 
 TEST_CASE("simgrid::mc::udpor::EventSet: Subset Tests")
 {
-  UnfoldingEvent e1, e2, e3, e4;
+  UnfoldingEvent e1;
+  UnfoldingEvent e2;
+  UnfoldingEvent e3;
+  UnfoldingEvent e4;
 
   // A is a subset of C only
   // B is a subset of C only
@@ -473,7 +493,12 @@ TEST_CASE("simgrid::mc::udpor::EventSet: Subset Tests")
   // D is NOT a subset of B
   // B is NOT a subset of D
   // ...
-  EventSet A{&e1, &e2, &e3}, B{&e2, &e3, &e4}, C{&e1, &e2, &e3, &e4}, D{&e1, &e3}, E{&e2, &e3}, F{&e1, &e2, &e3};
+  EventSet A{&e1, &e2, &e3};
+  EventSet B{&e2, &e3, &e4};
+  EventSet C{&e1, &e2, &e3, &e4};
+  EventSet D{&e1, &e3};
+  EventSet E{&e2, &e3};
+  EventSet F{&e1, &e2, &e3};
 
   SECTION("Subset operator properties")
   {
@@ -527,12 +552,12 @@ TEST_CASE("simgrid::mc::udpor::EventSet: Testing Configurations")
   // The tests enumerate all possible subsets of the events
   // in the structure and test whether those subsets are
   // maximal and/or valid configurations
-  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>());
-  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e4(EventSet({&e2}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e5(EventSet({&e1}), std::make_shared<IndependentAction>());
-  UnfoldingEvent e6(EventSet({&e5}), std::make_shared<IndependentAction>());
+  UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>(0));
+  UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>(1));
+  UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(2));
+  UnfoldingEvent e4(EventSet({&e2}), std::make_shared<IndependentAction>(3));
+  UnfoldingEvent e5(EventSet({&e1}), std::make_shared<IndependentAction>(4));
+  UnfoldingEvent e6(EventSet({&e5}), std::make_shared<IndependentAction>(5));
 
   SECTION("Valid Configurations")
   {
@@ -778,12 +803,12 @@ TEST_CASE("simgrid::mc::udpor::EventSet: Checking conflicts")
 
   SECTION("No conflicts throughout the whole structure with independent actions")
   {
-    UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>());
-    UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e4(EventSet({&e2}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e5(EventSet({&e1}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e6(EventSet({&e5}), std::make_shared<IndependentAction>());
+    UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>(0));
+    UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>(1));
+    UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(2));
+    UnfoldingEvent e4(EventSet({&e2}), std::make_shared<IndependentAction>(3));
+    UnfoldingEvent e5(EventSet({&e1}), std::make_shared<IndependentAction>(4));
+    UnfoldingEvent e6(EventSet({&e5}), std::make_shared<IndependentAction>(5));
 
     // 6 choose 0 = 1 test
     CHECK(EventSet().is_conflict_free());
@@ -958,12 +983,12 @@ TEST_CASE("simgrid::mc::udpor::EventSet: Checking conflicts")
 
   SECTION("Conditional conflicts")
   {
-    UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>());
-    UnfoldingEvent e2(EventSet({&e1}), std::make_shared<ConditionallyDependentAction>());
-    UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e4(EventSet({&e2}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e5(EventSet({&e1}), std::make_shared<DependentAction>());
-    UnfoldingEvent e6(EventSet({&e5}), std::make_shared<IndependentAction>());
+    UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>(0));
+    UnfoldingEvent e2(EventSet({&e1}), std::make_shared<ConditionallyDependentAction>(1));
+    UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(2));
+    UnfoldingEvent e4(EventSet({&e2}), std::make_shared<IndependentAction>(3));
+    UnfoldingEvent e5(EventSet({&e1}), std::make_shared<DependentAction>(4));
+    UnfoldingEvent e6(EventSet({&e5}), std::make_shared<IndependentAction>(5));
 
     // 6 choose 0 = 1 test
     // There are no events even to be in conflict with
diff --git a/src/mc/explo/udpor/ExtensionSetCalculator.cpp b/src/mc/explo/udpor/ExtensionSetCalculator.cpp
new file mode 100644 (file)
index 0000000..491ec2d
--- /dev/null
@@ -0,0 +1,379 @@
+/* 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/udpor/ExtensionSetCalculator.hpp"
+#include "src/mc/explo/udpor/Configuration.hpp"
+#include "src/mc/explo/udpor/History.hpp"
+#include "src/mc/explo/udpor/Unfolding.hpp"
+
+#include <functional>
+#include <unordered_map>
+#include <xbt/asserts.h>
+#include <xbt/ex.h>
+
+using namespace simgrid::mc;
+
+namespace simgrid::mc::udpor {
+
+EventSet ExtensionSetCalculator::partially_extend(const Configuration& C, Unfolding* U,
+                                                  const std::shared_ptr<Transition> action)
+{
+  using Action     = Transition::Type;
+  using Handler    = std::function<EventSet(const Configuration&, Unfolding*, const std::shared_ptr<Transition>)>;
+  using HandlerMap = std::unordered_map<Action, Handler>;
+
+  const static HandlerMap handlers =
+      HandlerMap{{Action::COMM_ASYNC_RECV, &ExtensionSetCalculator::partially_extend_CommRecv},
+                 {Action::COMM_ASYNC_SEND, &ExtensionSetCalculator::partially_extend_CommSend},
+                 {Action::COMM_WAIT, &ExtensionSetCalculator::partially_extend_CommWait}};
+
+  if (const auto handler = handlers.find(action->type_); handler != handlers.end()) {
+    return handler->second(C, U, std::move(action));
+  } else {
+    xbt_assert(false,
+               "There is currently no specialized computation for the transition "
+               "'%s' for computing extension sets in UDPOR, so the model checker cannot "
+               "determine how to proceed. Please submit a bug report requesting "
+               "that the transition be supported in SimGrid using UDPOR and consider "
+               "using the other model-checking algorithms supported by SimGrid instead "
+               "in the meantime",
+               action->to_string().c_str());
+    DIE_IMPOSSIBLE;
+  }
+}
+
+EventSet ExtensionSetCalculator::partially_extend_CommSend(const Configuration& C, Unfolding* U,
+                                                           const std::shared_ptr<Transition> action)
+{
+  EventSet exC;
+
+  const auto send_action        = std::static_pointer_cast<CommSendTransition>(std::move(action));
+  const auto pre_event_a_C      = C.pre_event(send_action->aid_);
+  const unsigned sender_mailbox = send_action->get_mailbox();
+
+  // 1. Create `e' := <a, config(preEvt(a, C))>` and add `e'` to `ex(C)`
+  // NOTE: If `preEvt(a, C)` doesn't exist, we're effectively asking
+  // about `config({})`
+  if (pre_event_a_C.has_value()) {
+    const auto e_prime = U->discover_event(EventSet({pre_event_a_C.value()}), send_action);
+    exC.insert(e_prime);
+  } else {
+    const auto e_prime = U->discover_event(EventSet(), send_action);
+    exC.insert(e_prime);
+  }
+
+  // 2. foreach e ∈ C s.t. λ(e) ∈ {AsyncSend(m, _), TestAny(Com)} where
+  // Com contains a matching c' = AsyncReceive(m, _) with a
+  for (const auto e : C) {
+    const bool transition_type_check = [&]() {
+      if (const auto* async_send = dynamic_cast<const CommSendTransition*>(e->get_transition());
+          async_send != nullptr) {
+        return async_send->get_mailbox() == sender_mailbox;
+      }
+      // TODO: Add `TestAny` dependency
+      return false;
+    }();
+
+    if (transition_type_check) {
+      const EventSet K = EventSet({e, pre_event_a_C.value_or(e)}).get_largest_maximal_subset();
+
+      // TODO: Check D_K(a, lambda(e))
+      if (true) {
+        const auto e_prime = U->discover_event(std::move(K), send_action);
+        exC.insert(e_prime);
+      }
+    }
+  }
+
+  // TODO: Add `TestAny` dependency case
+  return exC;
+}
+
+EventSet ExtensionSetCalculator::partially_extend_CommRecv(const Configuration& C, Unfolding* U,
+                                                           const std::shared_ptr<Transition> action)
+{
+  EventSet exC;
+
+  // TODO: if this is the first action by the actor, no such previous event exists.
+  // How do we react here? Do we say we're dependent with the root event?
+  const auto recv_action      = std::static_pointer_cast<CommRecvTransition>(std::move(action));
+  const unsigned recv_mailbox = recv_action->get_mailbox();
+  const auto pre_event_a_C    = C.pre_event(recv_action->aid_);
+
+  // 1. Create `e' := <a, config(preEvt(a, C))>` and add `e'` to `ex(C)`
+  if (pre_event_a_C.has_value()) {
+    const auto e_prime = U->discover_event(EventSet({pre_event_a_C.value()}), recv_action);
+    exC.insert(e_prime);
+  } else {
+    const auto e_prime = U->discover_event(EventSet(), recv_action);
+    exC.insert(e_prime);
+  }
+
+  // 2. foreach e ∈ C s.t. λ(e) ∈ {AsyncSend(m, _), TestAny(Com)} where
+  // Com contains a matching c' = AsyncReceive(m, _) with a
+  for (const auto e : C) {
+    const bool transition_type_check = [&]() {
+      if (const auto* async_recv = dynamic_cast<const CommRecvTransition*>(e->get_transition());
+          async_recv != nullptr && async_recv->get_mailbox() == recv_mailbox) {
+        return true;
+      }
+      // TODO: Add `TestAny` dependency
+      return false;
+    }();
+
+    if (transition_type_check) {
+      const EventSet K = EventSet({e, pre_event_a_C.value_or(e)}).get_largest_maximal_subset();
+
+      // TODO: Check D_K(a, lambda(e))
+      if (true) {
+        const auto e_prime = U->discover_event(std::move(K), recv_action);
+        exC.insert(e_prime);
+      }
+    }
+  }
+
+  // TODO: Add `TestAny` dependency case
+  return exC;
+}
+
+EventSet ExtensionSetCalculator::partially_extend_CommWait(const Configuration& C, Unfolding* U,
+                                                           std::shared_ptr<Transition> action)
+{
+  EventSet exC;
+
+  const auto wait_action   = std::static_pointer_cast<CommWaitTransition>(std::move(action));
+  const auto wait_comm     = wait_action->get_comm();
+  const auto pre_event_a_C = C.pre_event(wait_action->aid_);
+
+  // Determine the _issuer_ of the communication of the `CommWait` event
+  // in `C`. The issuer of the `CommWait` in `C` is the event in `C`
+  // whose transition is the `CommRecv` or `CommSend` whose resulting
+  // communication this `CommWait` waits on
+  const auto issuer = std::find_if(C.begin(), C.end(), [&](const UnfoldingEvent* e) {
+    if (const CommRecvTransition* e_issuer_receive = dynamic_cast<const CommRecvTransition*>(e->get_transition());
+        e_issuer_receive != nullptr) {
+      return e_issuer_receive->aid_ == wait_action->aid_ && wait_comm == e_issuer_receive->get_comm();
+    }
+
+    if (const CommSendTransition* e_issuer_send = dynamic_cast<const CommSendTransition*>(e->get_transition());
+        e_issuer_send != nullptr) {
+      return e_issuer_send->aid_ == wait_action->aid_ && wait_comm == e_issuer_send->get_comm();
+    }
+
+    return false;
+  });
+  xbt_assert(issuer != C.end(),
+             "Invariant violation! A (supposedly) enabled `CommWait` transition "
+             "waiting on commiunication %lu should not be enabled: the receive/send "
+             "transition which generated the communication is not an action taken "
+             "to reach state(C) (the state of the configuration), which should "
+             "be an impossibility if `%s` is enabled. Please report this as "
+             "a bug in SimGrid's UDPOR implementation",
+             wait_action->get_comm(), wait_action->to_string(false).c_str());
+  const UnfoldingEvent* e_issuer = *issuer;
+  const History e_issuer_history(e_issuer);
+
+  // 1. if `a` is enabled at state(config({preEvt(a,C)})), then
+  // create `e' := <a, config({preEvt(a,C)})>` and add `e'` to `ex(C)`
+  //
+  // First, if `pre_event_a_C == std::nullopt`, then there is nothing to
+  // do: `CommWait` will never be enabled in the empty configuration (at
+  // least two actions must be executed before)
+  if (pre_event_a_C.has_value(); const auto unwrapped_pre_event = pre_event_a_C.value()) {
+    // A necessary condition is that the issuer be present in
+    // config({preEvt(a, C)}); otherwise, the `CommWait` could not
+    // be enabled since the communication on which it waits would not
+    // have been created for it!
+    if (const auto config_pre_event = History(unwrapped_pre_event); config_pre_event.contains(e_issuer)) {
+      // If the issuer is a `CommRecv` (resp. `CommSend`), then we check that there
+      // are at least as many `CommSend` (resp. `CommRecv`) transitions in `config_pre_event`
+      // as needed to reach the receive/send number that is `issuer`.
+      // ...
+      // ...
+      if (const CommRecvTransition* e_issuer_receive =
+              dynamic_cast<const CommRecvTransition*>(e_issuer->get_transition());
+          e_issuer_receive != nullptr) {
+        const unsigned issuer_mailbox = e_issuer_receive->get_mailbox();
+
+        // Check from the config -> how many sends have there been
+        const unsigned send_position =
+            std::count_if(config_pre_event.begin(), config_pre_event.end(), [=](const auto e) {
+              const CommSendTransition* e_send = dynamic_cast<const CommSendTransition*>(e->get_transition());
+              if (e_send != nullptr) {
+                return e_send->get_mailbox() == issuer_mailbox;
+              }
+              return false;
+            });
+
+        // Check from e_issuer -> what place is the issuer in?
+        const unsigned receive_position =
+            std::count_if(e_issuer_history.begin(), e_issuer_history.end(), [=](const auto e) {
+              const CommRecvTransition* e_receive = dynamic_cast<const CommRecvTransition*>(e->get_transition());
+              if (e_receive != nullptr) {
+                return e_receive->get_mailbox() == issuer_mailbox;
+              }
+              return false;
+            });
+
+        if (send_position >= receive_position) {
+          exC.insert(U->discover_event(EventSet({unwrapped_pre_event}), wait_action));
+        }
+
+      } else if (const CommSendTransition* e_issuer_send =
+                     dynamic_cast<const CommSendTransition*>(e_issuer->get_transition());
+                 e_issuer_send != nullptr) {
+        const unsigned issuer_mailbox = e_issuer_send->get_mailbox();
+
+        // Check from e_issuer -> what place is the issuer in?
+        const unsigned send_position =
+            std::count_if(e_issuer_history.begin(), e_issuer_history.end(), [=](const auto e) {
+              const CommSendTransition* e_send = dynamic_cast<const CommSendTransition*>(e->get_transition());
+              if (e_send != nullptr) {
+                return e_send->get_mailbox() == issuer_mailbox;
+              }
+              return false;
+            });
+
+        // Check from the config -> how many sends have there been
+        const unsigned receive_position =
+            std::count_if(config_pre_event.begin(), config_pre_event.end(), [=](const auto e) {
+              const CommRecvTransition* e_receive = dynamic_cast<const CommRecvTransition*>(e->get_transition());
+              if (e_receive != nullptr) {
+                return e_receive->get_mailbox() == issuer_mailbox;
+              }
+              return false;
+            });
+
+        if (send_position <= receive_position) {
+          exC.insert(U->discover_event(EventSet({unwrapped_pre_event}), wait_action));
+        }
+
+      } else {
+        xbt_assert(false,
+                   "The transition which created the communication on which `%s` waits "
+                   "is neither an async send nor an async receive. The current UDPOR "
+                   "implementation does not know how to check if `CommWait` is enabled in "
+                   "this case. Was a new transition added?",
+                   e_issuer->get_transition()->to_string().c_str());
+      }
+    }
+  }
+
+  // 3. foreach event e in C do
+  for (const auto e : C) {
+    if (const CommSendTransition* e_issuer_send = dynamic_cast<const CommSendTransition*>(e_issuer->get_transition());
+        e_issuer_send != nullptr) {
+      // If the provider of the communication for `CommWait` is a
+      // `CommSend(m)`, then we only care about `e` if `λ(e) == `CommRecv(m)`.
+      // All other actions would be independent with the wait action (including
+      // another `CommSend` to the same mailbox: `CommWait` is "waiting" for its
+      // corresponding receive action)
+      if (e->get_transition()->type_ != Transition::Type::COMM_ASYNC_RECV) {
+        continue;
+      }
+
+      const auto issuer_mailbox        = e_issuer_send->get_mailbox();
+      const CommRecvTransition* e_recv = dynamic_cast<const CommRecvTransition*>(e->get_transition());
+      if (e_recv->get_mailbox() != issuer_mailbox) {
+        continue;
+      }
+
+      // If the `issuer` is not in `config(K)`, this implies that
+      // `WaitAny()` is always disabled in `config(K)`; hence, it
+      // is independent of any transition in `config(K)` (according
+      // to formal definition of independence)
+      const EventSet K    = EventSet({e, pre_event_a_C.value_or(e)});
+      const auto config_K = History(K);
+      if (not config_K.contains(e_issuer)) {
+        continue;
+      }
+
+      // TODO: Compute the send and receive positions
+
+      // What send # is the issuer
+      const unsigned send_position = std::count_if(e_issuer_history.begin(), e_issuer_history.end(), [=](const auto e) {
+        const CommSendTransition* e_send = dynamic_cast<const CommSendTransition*>(e->get_transition());
+        if (e_send != nullptr) {
+          return e_send->get_mailbox() == issuer_mailbox;
+        }
+        return false;
+      });
+
+      // What receive # is the event `e`?
+      const unsigned receive_position = std::count_if(config_K.begin(), config_K.end(), [=](const auto e) {
+        const CommRecvTransition* e_receive = dynamic_cast<const CommRecvTransition*>(e->get_transition());
+        if (e_receive != nullptr) {
+          return e_receive->get_mailbox() == issuer_mailbox;
+        }
+        return false;
+      });
+
+      if (send_position == receive_position) {
+        exC.insert(U->discover_event(std::move(K), wait_action));
+      }
+
+    } else if (const CommRecvTransition* e_issuer_recv =
+                   dynamic_cast<const CommRecvTransition*>(e_issuer->get_transition());
+               e_issuer_recv != nullptr) {
+      // If the provider of the communication for `CommWait` is a
+      // `CommRecv(m)`, then we only care about `e` if `λ(e) == `CommSend(m)`.
+      // All other actions would be independent with the wait action (including
+      // another `CommRecv` to the same mailbox: `CommWait` is "waiting" for its
+      // corresponding send action)
+      if (e->get_transition()->type_ != Transition::Type::COMM_ASYNC_SEND) {
+        continue;
+      }
+
+      const auto issuer_mailbox        = e_issuer_recv->get_mailbox();
+      const CommSendTransition* e_send = dynamic_cast<const CommSendTransition*>(e->get_transition());
+      if (e_send->get_mailbox() != issuer_mailbox) {
+        continue;
+      }
+
+      // If the `issuer` is not in `config(K)`, this implies that
+      // `WaitAny()` is always disabled in `config(K)`; hence, it
+      // is independent of any transition in `config(K)` (according
+      // to formal definition of independence)
+      const EventSet K    = EventSet({e, pre_event_a_C.value_or(e)});
+      const auto config_K = History(K);
+      if (not config_K.contains(e_issuer)) {
+        continue;
+      }
+
+      // What receive # is the event `e`?
+      const unsigned send_position = std::count_if(config_K.begin(), config_K.end(), [=](const auto e) {
+        const CommSendTransition* e_send = dynamic_cast<const CommSendTransition*>(e->get_transition());
+        if (e_send != nullptr) {
+          return e_send->get_mailbox() == issuer_mailbox;
+        }
+        return false;
+      });
+
+      // What send # is the issuer
+      const unsigned receive_position =
+          std::count_if(e_issuer_history.begin(), e_issuer_history.end(), [=](const auto e) {
+            const CommRecvTransition* e_receive = dynamic_cast<const CommRecvTransition*>(e->get_transition());
+            if (e_receive != nullptr) {
+              return e_receive->get_mailbox() == issuer_mailbox;
+            }
+            return false;
+          });
+
+      if (send_position == receive_position) {
+        exC.insert(U->discover_event(std::move(K), wait_action));
+      }
+    }
+  }
+
+  return exC;
+}
+
+EventSet ExtensionSetCalculator::partially_extend_CommTest(const Configuration& C, Unfolding* U,
+                                                           std::shared_ptr<Transition> test_action)
+{
+  return EventSet();
+}
+
+} // namespace simgrid::mc::udpor
diff --git a/src/mc/explo/udpor/ExtensionSetCalculator.hpp b/src/mc/explo/udpor/ExtensionSetCalculator.hpp
new file mode 100644 (file)
index 0000000..02b8669
--- /dev/null
@@ -0,0 +1,38 @@
+/* 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_UDPOR_EVENTSETCALCULATOR_HPP
+#define SIMGRID_MC_UDPOR_EVENTSETCALCULATOR_HPP
+
+#include "src/mc/explo/udpor/EventSet.hpp"
+#include "src/mc/explo/udpor/udpor_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::udpor {
+
+/**
+ * @brief Computes incrementally the portion of the extension set for a new configuration `C`
+ */
+struct ExtensionSetCalculator final {
+private:
+  static EventSet partially_extend_CommSend(const Configuration&, Unfolding*, std::shared_ptr<Transition>);
+  static EventSet partially_extend_CommRecv(const Configuration&, Unfolding*, std::shared_ptr<Transition>);
+  static EventSet partially_extend_CommWait(const Configuration&, Unfolding*, std::shared_ptr<Transition>);
+  static EventSet partially_extend_CommTest(const Configuration&, Unfolding*, std::shared_ptr<Transition>);
+
+public:
+  static EventSet partially_extend(const Configuration&, Unfolding*, const std::shared_ptr<Transition>);
+};
+
+} // namespace simgrid::mc::udpor
+#endif
index da35244..dc17c27 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <boost/iterator/iterator_facade.hpp>
 #include <functional>
+#include <initializer_list>
 #include <optional>
 
 namespace simgrid::mc::udpor {
@@ -54,8 +55,9 @@ public:
   History& operator=(History const&) = default;
   History(History&&)                 = default;
 
-  explicit History(EventSet event_set = EventSet()) : events_(std::move(event_set)) {}
   explicit History(const UnfoldingEvent* e) : events_({e}) {}
+  explicit History(EventSet event_set = EventSet()) : events_(std::move(event_set)) {}
+  explicit History(std::initializer_list<const UnfoldingEvent*> list) : events_(std::move(list)) {}
 
   auto begin() const { return Iterator(events_); }
   auto end() const { return Iterator(EventSet()); }
@@ -94,6 +96,17 @@ public:
    */
   EventSet get_all_maximal_events() const;
 
+  /**
+   * @brief Computes the set of events that are not contained
+   * in the given configuration
+   *
+   * A configuration is a causally-closed, conflict-free set
+   * of events. Thus, you can determine which events lie outside
+   * of a configuration during the search more efficiently: the moment
+   * you discover an event contained in the configuration, you
+   * do not need to search that event or any of its ancestors as
+   * they will all be contained in the configuration
+   */
   EventSet get_event_diff_with(const Configuration& config) const;
 
 private:
index 1b63328..421217e 100644 (file)
@@ -9,23 +9,23 @@
 
 namespace simgrid::mc::udpor {
 
-void Unfolding::remove(const EventSet& events)
+void Unfolding::mark_finished(const EventSet& events)
 {
   for (const auto e : events) {
-    remove(e);
+    mark_finished(e);
   }
 }
 
-void Unfolding::remove(const UnfoldingEvent* e)
+void Unfolding::mark_finished(const UnfoldingEvent* e)
 {
   if (e == nullptr) {
     throw std::invalid_argument("Expected a non-null pointer to an event, but received NULL");
   }
-  this->global_events_.erase(e);
-  this->event_handles.remove(e);
+  this->U.remove(e);
+  this->G.insert(e);
 }
 
-void Unfolding::insert(std::unique_ptr<UnfoldingEvent> e)
+const UnfoldingEvent* Unfolding::insert(std::unique_ptr<UnfoldingEvent> e)
 {
   const UnfoldingEvent* handle = e.get();
   if (auto loc = this->global_events_.find(handle); loc != this->global_events_.end()) {
@@ -36,29 +36,39 @@ void Unfolding::insert(std::unique_ptr<UnfoldingEvent> e)
                                 "This will result in a  double free error and must be fixed.");
   }
 
-  // Map the handle to its owner
-  this->event_handles.insert(handle);
-  this->global_events_[handle] = std::move(e);
-}
+  // Attempt to search first for an event in `U`. If it exists, we use that event
+  // instead of `e` since it is semantically equivalent to `e` (i.e. `e` is
+  // effectively already contained in the unfolding)
+  if (auto loc = std::find_if(U.begin(), U.end(), [=](const auto e_i) { return *e_i == *handle; }); loc != U.end()) {
+    // Return the handle to that event and ignore adding in a duplicate event
+    return *loc;
+  }
 
-bool Unfolding::contains_event_equivalent_to(const UnfoldingEvent* e) const
-{
-  // Notice the use of `==` equality here. `e` may not be contained in the
-  // unfolding; but some event which is "equivalent" to it could be.
-  for (const auto event : *this) {
-    if (*event == *e) {
-      return true;
-    }
+  // Then look for `e` in `G`. It's possible `e` was already constructed
+  // in the past, in which case we can simply re-use it.
+  //
+  // Note, though, that in this case we must move the event in `G` into
+  // `U`: we've inserted `e` into the unfolding, so we expect it to be in `U`
+  if (auto loc = std::find_if(G.begin(), G.end(), [=](const auto e_i) { return *e_i == *handle; }); loc != G.end()) {
+    const auto e_equiv = *loc;
+    G.remove(e_equiv);
+    U.insert(e_equiv);
+    return e_equiv;
   }
-  return false;
+
+  // Otherwise `e` is truly a "new" event
+  this->U.insert(handle);
+  this->event_handles.insert(handle);
+  this->global_events_[handle] = std::move(e);
+  return handle;
 }
 
 EventSet Unfolding::get_immediate_conflicts_of(const UnfoldingEvent* e) const
 {
   EventSet immediate_conflicts;
-  for (const auto event : *this) {
+  for (const auto event : U) {
     if (event->immediately_conflicts_with(e)) {
-      immediate_conflicts.insert(e);
+      immediate_conflicts.insert(event);
     }
   }
   return immediate_conflicts;
index 0107692..3fa80f2 100644 (file)
 namespace simgrid::mc::udpor {
 
 class Unfolding {
+public:
+  Unfolding()                       = default;
+  Unfolding& operator=(Unfolding&&) = default;
+  Unfolding(Unfolding&&)            = default;
+
+  auto begin() const { return this->event_handles.begin(); }
+  auto end() const { return this->event_handles.end(); }
+  auto cbegin() const { return this->event_handles.cbegin(); }
+  auto cend() const { return this->event_handles.cend(); }
+  size_t size() const { return this->event_handles.size(); }
+  bool empty() const { return this->event_handles.empty(); }
+
+  /**
+   * @brief Moves an event from UDPOR's global set `U` to
+   * the global set `G`
+   */
+  void mark_finished(const UnfoldingEvent* e);
+
+  /**
+   * @brief Moves all events in a set from UDPOR's global
+   * set `U` to the global set `G`
+   */
+  void mark_finished(const EventSet& events);
+
+  /// @brief Adds a new event `e` to the Unfolding if that
+  /// event is not equivalent to any of those already contained
+  /// in the unfolding
+  const UnfoldingEvent* insert(std::unique_ptr<UnfoldingEvent> e);
+
+  /**
+   * @brief Informs the unfolding of a (potentially) new event
+   *
+   * The unfolding of a concurrent program is a well-defined
+   * structure. Given the labeled transition system (LTS) of
+   * a program, the unfolding of that program can be determined
+   * algorithmically. However, UDPOR does not a priori know the structure of the
+   * unfolding as it performs its exploration. Thus, events in the
+   * unfolding are "discovered" as they are encountered, specifically
+   * when computing the extension sets of the configurations that
+   * UDPOR decides to search.
+   *
+   * This lends itself to the following problem: the extension sets
+   * of two different configurations may overlap one another. That
+   * is, for two configurations C and C' explored by UDPOR where C != C',
+   *
+   * ex(C) - ex(C') != empty
+   *
+   * Hence, when extending both `C` and `C'`, any events contained in
+   * the intersection of ex(C) and ex(C') will be attempted to be added
+   * twice. The unfolding will notice that these events have already
+   * been added and simply return the event already added to the unfolding
+   *
+   * @tparam ...Args arguments passed to the `UnfoldingEvent` constructor
+   * @return the handle to either the newly created event OR
+   * to an equivalent event that was already noted by the unfolding
+   * at some point in the past
+   */
+  template <typename... Args> const UnfoldingEvent* discover_event(Args&&... args)
+  {
+    auto candidate_event = std::make_unique<UnfoldingEvent>(std::forward<Args>(args)...);
+    return insert(std::move(candidate_event));
+  }
+
+  /// @brief Computes "#ⁱ_U(e)" for the given event, where `U` is the set
+  /// of the events in this unfolding
+  EventSet get_immediate_conflicts_of(const UnfoldingEvent*) const;
+
 private:
   /**
    * @brief All of the events that are currently are a part of the unfolding
    *
    * @invariant Each unfolding event maps itself to the owner of that event,
-   * i.e. the unique pointer that owns the address. The Unfolding owns all
+   * i.e. the unique pointer that manages the data at the address. The Unfolding owns all
    * of the addresses that are referenced by EventSet instances and Configuration
    * instances. UDPOR guarantees that events are persisted for as long as necessary
    */
@@ -32,40 +99,24 @@ private:
    *
    * @invariant: All of the events in this set are elements of `global_events_`
    * and is kept updated at the same time as `global_events_`
+   *
+   * @note: This is for the convenience of iteration over the unfolding
    */
   EventSet event_handles;
 
+  /**
+   * @brief: The collection of events in the unfolding that are "important"
+   */
+  EventSet U;
+
   /**
    * @brief The "irrelevant" portions of the unfolding that do not need to be kept
    * around to ensure that UDPOR functions correctly
    *
    * The set `G` is another global variable maintained by the UDPOR algorithm which
    * is used to keep track of all events which used to be important to UDPOR.
-   *
-   * @note: The current implementation does not touch the set `G`. Its use is perhaps
-   * limited to debugging and/or model-checking acyclic state spaces
    */
   EventSet G;
-
-public:
-  Unfolding()                       = default;
-  Unfolding& operator=(Unfolding&&) = default;
-  Unfolding(Unfolding&&)            = default;
-
-  void remove(const UnfoldingEvent* e);
-  void remove(const EventSet& events);
-  void insert(std::unique_ptr<UnfoldingEvent> e);
-  bool contains_event_equivalent_to(const UnfoldingEvent* e) const;
-
-  auto begin() const { return this->event_handles.begin(); }
-  auto end() const { return this->event_handles.end(); }
-  auto cbegin() const { return this->event_handles.cbegin(); }
-  auto cend() const { return this->event_handles.cend(); }
-  size_t size() const { return this->global_events_.size(); }
-  bool empty() const { return this->global_events_.empty(); }
-
-  /// @brief Computes "#ⁱ_U(e)" for the given event
-  EventSet get_immediate_conflicts_of(const UnfoldingEvent*) const;
 };
 
 } // namespace simgrid::mc::udpor
index c51023b..b1d7382 100644 (file)
@@ -6,6 +6,10 @@
 #include "src/mc/explo/udpor/UnfoldingEvent.hpp"
 #include "src/mc/explo/udpor/History.hpp"
 
+#include <xbt/asserts.h>
+#include <xbt/log.h>
+#include <xbt/string.hpp>
+
 namespace simgrid::mc::udpor {
 
 UnfoldingEvent::UnfoldingEvent(std::initializer_list<const UnfoldingEvent*> init_list)
@@ -20,6 +24,10 @@ UnfoldingEvent::UnfoldingEvent(EventSet immediate_causes, std::shared_ptr<Transi
 
 bool UnfoldingEvent::operator==(const UnfoldingEvent& other) const
 {
+  // Intrinsic identity check
+  if (this == &other) {
+    return true;
+  }
   // Two events are equivalent iff:
   // 1. they have the same action
   // 2. they have the same history
@@ -36,7 +44,29 @@ bool UnfoldingEvent::operator==(const UnfoldingEvent& other) const
          this->immediate_causes == other.immediate_causes;
 }
 
+std::string UnfoldingEvent::to_string() const
+{
+  std::string dependencies_string;
+
+  dependencies_string += "[";
+  for (const auto* e : immediate_causes) {
+    dependencies_string += e->to_string();
+  }
+  dependencies_string += "]";
+
+  return xbt::string_printf("(%p) Actor %ld: %s (%zu dependencies: %s)", this, associated_transition->aid_,
+                            associated_transition->to_string().c_str(), immediate_causes.size(),
+                            dependencies_string.c_str());
+}
+
 EventSet UnfoldingEvent::get_history() const
+{
+  EventSet local_config = get_local_config();
+  local_config.remove(this);
+  return local_config;
+}
+
+EventSet UnfoldingEvent::get_local_config() const
 {
   return History(this).get_all_events();
 }
@@ -60,8 +90,8 @@ bool UnfoldingEvent::conflicts_with(const UnfoldingEvent* other) const
     return false;
   }
 
-  const EventSet my_history      = get_history();
-  const EventSet other_history   = other->get_history();
+  const EventSet my_history      = get_local_config();
+  const EventSet other_history   = other->get_local_config();
   const EventSet unique_to_me    = my_history.subtracting(other_history);
   const EventSet unique_to_other = other_history.subtracting(my_history);
 
@@ -72,16 +102,9 @@ bool UnfoldingEvent::conflicts_with(const UnfoldingEvent* other) const
   return conflicts_with_me or conflicts_with_other;
 }
 
-bool UnfoldingEvent::conflicts_with(const Configuration& config) const
+bool UnfoldingEvent::conflicts_with_any(const EventSet& events) const
 {
-  // A configuration is itself already conflict-free. Thus, it is
-  // simply a matter of testing whether or not the transition associated
-  // with the event is dependent with any already in `config` that are
-  // OUTSIDE this event's history (in an unfolding, events only conflict
-  // if they are not related)
-  const EventSet potential_conflicts = config.get_events().subtracting(get_history());
-  return std::any_of(potential_conflicts.cbegin(), potential_conflicts.cend(),
-                     [&](const UnfoldingEvent* e) { return this->is_dependent_with(e); });
+  return std::any_of(events.begin(), events.end(), [&](const auto e) { return e->conflicts_with(this); });
 }
 
 bool UnfoldingEvent::immediately_conflicts_with(const UnfoldingEvent* other) const
index aeb4902..bad411c 100644 (file)
@@ -27,7 +27,13 @@ public:
   UnfoldingEvent(UnfoldingEvent&&)                 = default;
 
   EventSet get_history() const;
+  EventSet get_local_config() const;
   bool in_history_of(const UnfoldingEvent* other) const;
+
+  /**
+   * @brief Whether or not the given event is a decendant
+   * of or an ancestor of the given event
+   */
   bool related_to(const UnfoldingEvent* other) const;
 
   /// @brief Whether or not this event is in conflict with
@@ -35,8 +41,8 @@ public:
   bool conflicts_with(const UnfoldingEvent* other) const;
 
   /// @brief Whether or not this event is in conflict with
-  /// any event in the given configuration
-  bool conflicts_with(const Configuration& config) const;
+  /// any event in the given set
+  bool conflicts_with_any(const EventSet& events) const;
 
   /// @brief Computes "this #ⁱ other"
   bool immediately_conflicts_with(const UnfoldingEvent* other) const;
@@ -45,6 +51,11 @@ public:
 
   const EventSet& get_immediate_causes() const { return this->immediate_causes; }
   Transition* get_transition() const { return this->associated_transition.get(); }
+  aid_t get_actor() const { return get_transition()->aid_; }
+
+  void set_transition(std::shared_ptr<Transition> t) { this->associated_transition = std::move(t); }
+
+  std::string to_string() const;
 
   bool operator==(const UnfoldingEvent&) const;
   bool operator!=(const UnfoldingEvent& other) const { return not(*this == other); }
index a676c69..df316fd 100644 (file)
@@ -119,13 +119,13 @@ TEST_CASE("simgrid::mc::udpor::UnfoldingEvent: Dependency/Conflict Tests")
     //        e4  e5   e7
     //
     // e5 and e6 are in conflict, e5 and e7 are in conflict, e2 and e6, and e2 ands e7 are in conflict
-    UnfoldingEvent e1(EventSet(), std::make_shared<ConditionallyDependentAction>());
-    UnfoldingEvent e2(EventSet({&e1}), std::make_shared<DependentAction>());
-    UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e4(EventSet({&e3}), std::make_shared<ConditionallyDependentAction>());
-    UnfoldingEvent e5(EventSet({&e3}), std::make_shared<DependentAction>());
-    UnfoldingEvent e6(EventSet({&e1}), std::make_shared<ConditionallyDependentAction>());
-    UnfoldingEvent e7(EventSet({&e6, &e2}), std::make_shared<ConditionallyDependentAction>());
+    UnfoldingEvent e1(EventSet(), std::make_shared<ConditionallyDependentAction>(0));
+    UnfoldingEvent e2(EventSet({&e1}), std::make_shared<DependentAction>(0));
+    UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(0));
+    UnfoldingEvent e4(EventSet({&e3}), std::make_shared<ConditionallyDependentAction>(1));
+    UnfoldingEvent e5(EventSet({&e3}), std::make_shared<DependentAction>(1));
+    UnfoldingEvent e6(EventSet({&e1}), std::make_shared<ConditionallyDependentAction>(2));
+    UnfoldingEvent e7(EventSet({&e6, &e2}), std::make_shared<ConditionallyDependentAction>(3));
 
     SECTION("Dependency relation properties")
     {
@@ -180,7 +180,7 @@ TEST_CASE("simgrid::mc::udpor::UnfoldingEvent: Dependency/Conflict Tests")
     }
   }
 
-  SECTION("No conflicts whatsoever")
+  SECTION("Testing with no dependencies whatsoever")
   {
     // The following tests concern the given event structure:
     //                e1
@@ -190,19 +190,20 @@ TEST_CASE("simgrid::mc::udpor::UnfoldingEvent: Dependency/Conflict Tests")
     //          e3   /   /
     //         /  /    /
     //        e4  e5   e7
-    UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>());
-    UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e5(EventSet({&e3}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e6(EventSet({&e1}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e7(EventSet({&e6, &e2}), std::make_shared<IndependentAction>());
+    UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>(0));
+    UnfoldingEvent e2(EventSet({&e1}), std::make_shared<IndependentAction>(1));
+    UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(2));
+    UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>(3));
+    UnfoldingEvent e5(EventSet({&e3}), std::make_shared<IndependentAction>(4));
+    UnfoldingEvent e6(EventSet({&e1}), std::make_shared<IndependentAction>(5));
+    UnfoldingEvent e7(EventSet({&e6, &e2}), std::make_shared<IndependentAction>(6));
 
     // Since everyone's actions are independent of one another, we expect
-    // that there are no conflicts between each pair of events
+    // that there are no conflicts between each pair of events (except with
+    // the same event itself)
     SECTION("Mutual dependencies")
     {
-      CHECK_FALSE(e1.is_dependent_with(&e1));
+      CHECK(e1.is_dependent_with(&e1));
       CHECK_FALSE(e1.is_dependent_with(&e2));
       CHECK_FALSE(e1.is_dependent_with(&e3));
       CHECK_FALSE(e1.is_dependent_with(&e4));
@@ -210,32 +211,32 @@ TEST_CASE("simgrid::mc::udpor::UnfoldingEvent: Dependency/Conflict Tests")
       CHECK_FALSE(e1.is_dependent_with(&e6));
       CHECK_FALSE(e1.is_dependent_with(&e7));
 
-      CHECK_FALSE(e2.is_dependent_with(&e2));
+      CHECK(e2.is_dependent_with(&e2));
       CHECK_FALSE(e2.is_dependent_with(&e3));
       CHECK_FALSE(e2.is_dependent_with(&e4));
       CHECK_FALSE(e2.is_dependent_with(&e5));
       CHECK_FALSE(e2.is_dependent_with(&e6));
       CHECK_FALSE(e2.is_dependent_with(&e7));
 
-      CHECK_FALSE(e3.is_dependent_with(&e3));
+      CHECK(e3.is_dependent_with(&e3));
       CHECK_FALSE(e3.is_dependent_with(&e4));
       CHECK_FALSE(e3.is_dependent_with(&e5));
       CHECK_FALSE(e3.is_dependent_with(&e6));
       CHECK_FALSE(e3.is_dependent_with(&e7));
 
-      CHECK_FALSE(e4.is_dependent_with(&e4));
+      CHECK(e4.is_dependent_with(&e4));
       CHECK_FALSE(e4.is_dependent_with(&e5));
       CHECK_FALSE(e4.is_dependent_with(&e6));
       CHECK_FALSE(e4.is_dependent_with(&e7));
 
-      CHECK_FALSE(e5.is_dependent_with(&e5));
+      CHECK(e5.is_dependent_with(&e5));
       CHECK_FALSE(e5.is_dependent_with(&e6));
       CHECK_FALSE(e5.is_dependent_with(&e7));
 
-      CHECK_FALSE(e6.is_dependent_with(&e6));
+      CHECK(e6.is_dependent_with(&e6));
       CHECK_FALSE(e6.is_dependent_with(&e7));
 
-      CHECK_FALSE(e7.is_dependent_with(&e7));
+      CHECK(e7.is_dependent_with(&e7));
     }
 
     SECTION("Mutual conflicts")
@@ -298,7 +299,7 @@ TEST_CASE("simgrid::mc::udpor::UnfoldingEvent: Dependency/Conflict Tests")
     }
   }
 
-  SECTION("General conflicts")
+  SECTION("Testing with some conflicts")
   {
     // The following tests concern the given event structure:
     //                e1
@@ -308,16 +309,17 @@ TEST_CASE("simgrid::mc::udpor::UnfoldingEvent: Dependency/Conflict Tests")
     //          e3   /   /
     //         /  /    /
     //        e4  e5   e7
-    UnfoldingEvent e1(EventSet(), std::make_shared<DependentAction>());
-    UnfoldingEvent e2(EventSet({&e1}), std::make_shared<DependentAction>());
-    UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e5(EventSet({&e3}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e6(EventSet({&e1}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e7(EventSet({&e6, &e2}), std::make_shared<ConditionallyDependentAction>());
+    UnfoldingEvent e1(EventSet(), std::make_shared<DependentAction>(0));
+    UnfoldingEvent e2(EventSet({&e1}), std::make_shared<DependentAction>(1));
+    UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(2));
+    UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>(3));
+    UnfoldingEvent e5(EventSet({&e3}), std::make_shared<IndependentAction>(4));
+    UnfoldingEvent e6(EventSet({&e1}), std::make_shared<IndependentAction>(5));
+    UnfoldingEvent e7(EventSet({&e6, &e2}), std::make_shared<ConditionallyDependentAction>(6));
 
     // Since everyone's actions are independent of one another, we expect
-    // that there are no conflicts between each pair of events
+    // that there are no conflicts between each pair of events (except the pair
+    // with the event and itself)
     SECTION("Mutual dependencies")
     {
       CHECK(e1.is_dependent_with(&e1));
@@ -335,25 +337,25 @@ TEST_CASE("simgrid::mc::udpor::UnfoldingEvent: Dependency/Conflict Tests")
       CHECK_FALSE(e2.is_dependent_with(&e6));
       CHECK(e2.is_dependent_with(&e7));
 
-      CHECK_FALSE(e3.is_dependent_with(&e3));
+      CHECK(e3.is_dependent_with(&e3));
       CHECK_FALSE(e3.is_dependent_with(&e4));
       CHECK_FALSE(e3.is_dependent_with(&e5));
       CHECK_FALSE(e3.is_dependent_with(&e6));
       CHECK_FALSE(e3.is_dependent_with(&e7));
 
-      CHECK_FALSE(e4.is_dependent_with(&e4));
+      CHECK(e4.is_dependent_with(&e4));
       CHECK_FALSE(e4.is_dependent_with(&e5));
       CHECK_FALSE(e4.is_dependent_with(&e6));
       CHECK_FALSE(e4.is_dependent_with(&e7));
 
-      CHECK_FALSE(e5.is_dependent_with(&e5));
+      CHECK(e5.is_dependent_with(&e5));
       CHECK_FALSE(e5.is_dependent_with(&e6));
       CHECK_FALSE(e5.is_dependent_with(&e7));
 
-      CHECK_FALSE(e6.is_dependent_with(&e6));
+      CHECK(e6.is_dependent_with(&e6));
       CHECK_FALSE(e6.is_dependent_with(&e7));
 
-      CHECK_FALSE(e7.is_dependent_with(&e7));
+      CHECK(e7.is_dependent_with(&e7));
     }
 
     SECTION("Mutual conflicts")
@@ -430,13 +432,13 @@ TEST_CASE("simgrid::mc::udpor::UnfoldingEvent: Dependency/Conflict Tests")
     //          e3      /
     //         /  /    e7
     //        e4  e5
-    UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>());
-    UnfoldingEvent e2(EventSet({&e1}), std::make_shared<ConditionallyDependentAction>());
-    UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e5(EventSet({&e3}), std::make_shared<IndependentAction>());
-    UnfoldingEvent e6(EventSet({&e1}), std::make_shared<DependentAction>());
-    UnfoldingEvent e7(EventSet({&e6}), std::make_shared<IndependentAction>());
+    UnfoldingEvent e1(EventSet(), std::make_shared<IndependentAction>(0));
+    UnfoldingEvent e2(EventSet({&e1}), std::make_shared<ConditionallyDependentAction>(1));
+    UnfoldingEvent e3(EventSet({&e2}), std::make_shared<IndependentAction>(2));
+    UnfoldingEvent e4(EventSet({&e3}), std::make_shared<IndependentAction>(3));
+    UnfoldingEvent e5(EventSet({&e3}), std::make_shared<IndependentAction>(4));
+    UnfoldingEvent e6(EventSet({&e1}), std::make_shared<DependentAction>(5));
+    UnfoldingEvent e7(EventSet({&e6}), std::make_shared<IndependentAction>(6));
 
     CHECK_FALSE(e1.conflicts_with(&e1));
     CHECK_FALSE(e1.conflicts_with(&e2));
index d75fb28..30e1529 100644 (file)
@@ -17,11 +17,13 @@ TEST_CASE("simgrid::mc::udpor::Unfolding: Creating an unfolding")
   REQUIRE(unfolding.empty());
 }
 
-TEST_CASE("simgrid::mc::udpor::Unfolding: Inserting and removing events with an unfolding")
+TEST_CASE("simgrid::mc::udpor::Unfolding: Inserting and marking events with an unfolding")
 {
   Unfolding unfolding;
-  auto e1              = std::make_unique<UnfoldingEvent>();
-  auto e2              = std::make_unique<UnfoldingEvent>();
+  auto e1 = std::make_unique<UnfoldingEvent>(
+      EventSet(), std::make_shared<ConditionallyDependentAction>(Transition::Type::UNKNOWN, 0));
+  auto e2 =
+      std::make_unique<UnfoldingEvent>(EventSet(), std::make_shared<DependentAction>(Transition::Type::UNKNOWN, 1));
   const auto e1_handle = e1.get();
   const auto e2_handle = e2.get();
 
@@ -33,32 +35,13 @@ TEST_CASE("simgrid::mc::udpor::Unfolding: Inserting and removing events with an
   REQUIRE(unfolding.size() == 2);
   REQUIRE_FALSE(unfolding.empty());
 
-  unfolding.remove(e1_handle);
-  REQUIRE(unfolding.size() == 1);
+  unfolding.mark_finished(e1_handle);
+  REQUIRE(unfolding.size() == 2);
   REQUIRE_FALSE(unfolding.empty());
 
-  unfolding.remove(e2_handle);
-  REQUIRE(unfolding.size() == 0);
-  REQUIRE(unfolding.empty());
-}
-
-TEST_CASE("simgrid::mc::udpor::Unfolding: Checking for semantically equivalent events")
-{
-  Unfolding unfolding;
-  auto e1 = std::make_unique<UnfoldingEvent>(
-      EventSet(), std::make_shared<IndependentAction>(Transition::Type::BARRIER_ASYNC_LOCK, 6, 2));
-  auto e2 = std::make_unique<UnfoldingEvent>(
-      EventSet(), std::make_shared<IndependentAction>(Transition::Type::BARRIER_ASYNC_LOCK, 6, 2));
-
-  // e1 and e2 are equivalent
-  REQUIRE(*e1 == *e2);
-
-  const auto e1_handle = e1.get();
-  const auto e2_handle = e2.get();
-  unfolding.insert(std::move(e1));
-
-  REQUIRE(unfolding.contains_event_equivalent_to(e1_handle));
-  REQUIRE(unfolding.contains_event_equivalent_to(e2_handle));
+  unfolding.mark_finished(e2_handle);
+  REQUIRE(unfolding.size() == 2);
+  REQUIRE_FALSE(unfolding.empty());
 }
 
 TEST_CASE("simgrid::mc::udpor::Unfolding: Checking all immediate conflicts restricted to an unfolding") {}
\ No newline at end of file
index d6ed988..b613955 100644 (file)
@@ -110,7 +110,7 @@ maximal_subsets_iterator::continue_traversal_of_maximal_events_tree()
   return topological_ordering.end();
 }
 
-bool maximal_subsets_iterator::bookkeeper::is_candidate_event(const UnfoldingEvent* e) const
+bool maximal_subsets_iterator::Bookkeeper::is_candidate_event(const UnfoldingEvent* e) const
 {
   if (const auto e_count = event_counts.find(e); e_count != event_counts.end()) {
     return e_count->second == 0;
@@ -153,24 +153,24 @@ bool maximal_subsets_iterator::can_grow_maximal_set() const
 }
 
 maximal_subsets_iterator::topological_order_position
-maximal_subsets_iterator::bookkeeper::find_next_candidate_event(topological_order_position first,
+maximal_subsets_iterator::Bookkeeper::find_next_candidate_event(topological_order_position first,
                                                                 topological_order_position last) const
 {
   return std::find_if(first, last, [&](const UnfoldingEvent* e) { return is_candidate_event(e); });
 }
 
-void maximal_subsets_iterator::bookkeeper::mark_included_in_maximal_set(const UnfoldingEvent* e)
+void maximal_subsets_iterator::Bookkeeper::mark_included_in_maximal_set(const UnfoldingEvent* e)
 {
-  const auto e_history = e->get_history();
-  for (const auto e_hist : e_history) {
+  const auto e_local_config = e->get_local_config();
+  for (const auto e_hist : e_local_config) {
     event_counts[e_hist]++;
   }
 }
 
-void maximal_subsets_iterator::bookkeeper::mark_removed_from_maximal_set(const UnfoldingEvent* e)
+void maximal_subsets_iterator::Bookkeeper::mark_removed_from_maximal_set(const UnfoldingEvent* e)
 {
-  const auto e_history = e->get_history();
-  for (const auto e_hist : e_history) {
+  const auto e_local_config = e->get_local_config();
+  for (const auto e_hist : e_local_config) {
     xbt_assert(event_counts.find(e_hist) != event_counts.end(),
                "Invariant Violation: Attempted to remove an event which was not previously added");
     xbt_assert(event_counts[e_hist] > 0, "Invariant Violation: An event `e` had a count of `0` at this point "
@@ -182,4 +182,4 @@ void maximal_subsets_iterator::bookkeeper::mark_removed_from_maximal_set(const U
   }
 }
 
-} // namespace simgrid::mc::udpor
\ No newline at end of file
+} // namespace simgrid::mc::udpor
index 7d0ab46..2c3c07f 100644 (file)
@@ -38,7 +38,7 @@ public:
   using node_filter_function       = std::function<bool(const UnfoldingEvent*)>;
   using topological_order_position = std::vector<const UnfoldingEvent*>::const_iterator;
 
-  maximal_subsets_iterator() = default;
+  maximal_subsets_iterator()                                    = default;
   explicit maximal_subsets_iterator(const Configuration& config,
                                     std::optional<node_filter_function> filter = std::nullopt,
                                     std::optional<size_t> maximum_subset_size  = std::nullopt)
@@ -58,7 +58,7 @@ private:
   bool has_started_searching                              = false;
   std::optional<size_t> maximum_subset_size               = std::nullopt;
   std::optional<EventSet> current_maximal_set             = std::nullopt;
-  std::stack<topological_order_position> backtrack_points = std::stack<topological_order_position>();
+  std::stack<topological_order_position, std::vector<topological_order_position>> backtrack_points;
 
   /**
    * @brief A small class which provides functionality for managing
@@ -70,7 +70,7 @@ private:
    * with events that are its current maximal event set (i.e.
    * its `current_maximal_set`)
    */
-  struct bookkeeper {
+  struct Bookkeeper {
   public:
     using topological_order_position = maximal_subsets_iterator::topological_order_position;
 
@@ -86,7 +86,8 @@ private:
     /// bookkeeping that has been done thus far, can be added to the
     /// current candidate maximal set
     bool is_candidate_event(const UnfoldingEvent*) const;
-  } bookkeeper;
+  };
+  Bookkeeper bookkeeper;
 
   void add_element_to_current_maximal_set(const UnfoldingEvent*);
   void remove_element_from_current_maximal_set(const UnfoldingEvent*);
index 1cbdb2e..23c13f3 100644 (file)
 #ifndef SIMGRID_MC_UDPOR_FORWARD_HPP
 #define SIMGRID_MC_UDPOR_FORWARD_HPP
 
+#include "src/mc/mc_forward.hpp"
+#include <simgrid/forward.h>
+
 namespace simgrid::mc::udpor {
 
+class Comb;
+class ExtensionSetCalculator;
 class EventSet;
 class Configuration;
 class History;
index 276edfc..de2ed37 100644 (file)
@@ -18,33 +18,59 @@ namespace simgrid::mc::udpor {
 
 struct IndependentAction : public Transition {
   IndependentAction() = default;
-  IndependentAction(Type type, aid_t issuer, int times_considered) : Transition(type, issuer, times_considered) {}
+  IndependentAction(Type type, aid_t issuer, int times_considered = 0) : Transition(type, issuer, times_considered) {}
+  IndependentAction(aid_t issuer, int times_considered = 0)
+      : IndependentAction(simgrid::mc::Transition::Type::UNKNOWN, issuer, times_considered)
+  {
+  }
 
-  // Independent with everyone else
-  bool depends(const Transition* other) const override { return false; }
+  // Independent with everyone else (even if run by the same actor). NOTE: This is
+  // only for the convenience of testing: in general, transitions are dependent with
+  // one another if run by the same actor
+  bool depends(const Transition* other) const override
+  {
+    if (aid_ == other->aid_) {
+      return true;
+    }
+    return false;
+  }
 };
 
 struct DependentAction : public Transition {
   DependentAction() = default;
-  DependentAction(Type type, aid_t issuer, int times_considered) : Transition(type, issuer, times_considered) {}
+  DependentAction(Type type, aid_t issuer, int times_considered = 0) : Transition(type, issuer, times_considered) {}
+  DependentAction(aid_t issuer, int times_considered = 0)
+      : DependentAction(simgrid::mc::Transition::Type::UNKNOWN, issuer, times_considered)
+  {
+  }
 
   // Dependent with everyone else (except IndependentAction)
   bool depends(const Transition* other) const override
   {
+    if (aid_ == other->aid_) {
+      return true;
+    }
     return dynamic_cast<const IndependentAction*>(other) == nullptr;
   }
 };
 
 struct ConditionallyDependentAction : public Transition {
   ConditionallyDependentAction() = default;
-  ConditionallyDependentAction(Type type, aid_t issuer, int times_considered)
+  ConditionallyDependentAction(Type type, aid_t issuer, int times_considered = 0)
       : Transition(type, issuer, times_considered)
   {
   }
+  ConditionallyDependentAction(aid_t issuer, int times_considered = 0)
+      : ConditionallyDependentAction(simgrid::mc::Transition::Type::UNKNOWN, issuer, times_considered)
+  {
+  }
 
   // Dependent only with DependentAction (i.e. not itself)
   bool depends(const Transition* other) const override
   {
+    if (aid_ == other->aid_) {
+      return true;
+    }
     return dynamic_cast<const DependentAction*>(other) != nullptr;
   }
 };
index d9882f0..d7e0c92 100644 (file)
@@ -216,7 +216,7 @@ unw_addr_space_t UnwindContext::createUnwindAddressSpace()
   return unw_create_addr_space(&accessors, BYTE_ORDER);
 }
 
-void UnwindContext::initialize(simgrid::mc::RemoteProcessMemory& process_memory, unw_context_t* c)
+void UnwindContext::initialize(simgrid::mc::RemoteProcessMemory& process_memory, const unw_context_t* c)
 {
   this->address_space_ = &process_memory;
   this->process_       = &process_memory;
index 7b9babb..6291c1c 100644 (file)
@@ -45,7 +45,7 @@ class UnwindContext {
   unw_context_t unwind_context_              = {};
 
 public:
-  void initialize(simgrid::mc::RemoteProcessMemory& process, unw_context_t* c);
+  void initialize(simgrid::mc::RemoteProcessMemory& process, const unw_context_t* c);
   unw_cursor_t cursor();
 
 private: // Methods and virtual table for libunwind
index acd042d..6e4ecf1 100644 (file)
@@ -8,13 +8,12 @@
 #include "src/kernel/activity/CommImpl.hpp"
 #include "src/kernel/activity/MutexImpl.hpp"
 #include "src/kernel/actor/SimcallObserver.hpp"
+
 #include "src/mc/mc.h"
 #include "src/mc/mc_config.hpp"
 #include "src/mc/mc_replay.hpp"
 
-#if SIMGRID_HAVE_MC
-#include "src/mc/api/RemoteApp.hpp"
-#include "src/mc/remote/AppSide.hpp"
+#if SIMGRID_HAVE_STATEFUL_MC
 #include "src/mc/sosp/RemoteProcessMemory.hpp"
 #endif
 
@@ -52,10 +51,8 @@ void execute_actors()
  */
 bool actor_is_enabled(kernel::actor::ActorImpl* actor)
 {
-#if SIMGRID_HAVE_MC
-  xbt_assert(simgrid::mc::model_checking_mode != simgrid::mc::ModelCheckingMode::CHECKER_SIDE,
+  xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
              "This should be called from the client side");
-#endif
 
   // Now, we are in the client app, no need for remote memory reading.
   kernel::actor::Simcall* req = &actor->simcall_;
@@ -75,10 +72,9 @@ bool actor_is_enabled(kernel::actor::ActorImpl* actor)
  */
 bool request_is_visible(const kernel::actor::Simcall* req)
 {
-#if SIMGRID_HAVE_MC
-  xbt_assert(simgrid::mc::model_checking_mode != simgrid::mc::ModelCheckingMode::CHECKER_SIDE,
+  xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
              "This should be called from the client side");
-#endif
+
   if (req->observer_ == nullptr)
     return false;
   return req->observer_->is_visible();
index c257a76..b0bed22 100644 (file)
 #include "xbt/asserts.h"
 #include "xbt/random.hpp"
 
+using namespace simgrid::mc;
+
 /* Implementation of the user API from the App to the Checker (see modelchecker.h)  */
 
 int MC_random(int min, int max)
 {
-#if SIMGRID_HAVE_MC
-  xbt_assert(simgrid::mc::model_checking_mode != simgrid::mc::ModelCheckingMode::CHECKER_SIDE,
+  xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
              "This should be called from the client side");
-#endif
+
   if (not MC_is_active() && not MC_record_replay_is_active()) { // no need to do a simcall in this case
     static simgrid::xbt::random::XbtRandom prng;
     return prng.uniform_int(min, max);
@@ -32,12 +33,12 @@ int MC_random(int min, int max)
 void MC_assert(int prop)
 {
   // Cannot used xbt_assert here, or it would be an infinite recursion.
-#if SIMGRID_HAVE_MC
-  xbt_assert(simgrid::mc::model_checking_mode != simgrid::mc::ModelCheckingMode::CHECKER_SIDE,
+  xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
              "This should be called from the client side");
+#if SIMGRID_HAVE_MC
   if (not prop) {
     if (MC_is_active())
-      simgrid::mc::AppSide::get()->report_assertion_failure();
+      AppSide::get()->report_assertion_failure();
     if (MC_record_replay_is_active())
       xbt_die("MC assertion failed");
   }
@@ -49,42 +50,46 @@ void MC_assert(int prop)
 
 int MC_is_active()
 {
-  return simgrid::mc::model_checking_mode == simgrid::mc::ModelCheckingMode::APP_SIDE ||
-         simgrid::mc::model_checking_mode == simgrid::mc::ModelCheckingMode::CHECKER_SIDE;
+  return get_model_checking_mode() == ModelCheckingMode::APP_SIDE ||
+         get_model_checking_mode() == ModelCheckingMode::CHECKER_SIDE;
 }
 
 void MC_automaton_new_propositional_symbol_pointer(const char *name, int* value)
 {
-#if SIMGRID_HAVE_MC
-  xbt_assert(simgrid::mc::model_checking_mode != simgrid::mc::ModelCheckingMode::CHECKER_SIDE,
+#if SIMGRID_HAVE_STATEFUL_MC
+  xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
              "This should be called from the client side");
-  simgrid::mc::AppSide::get()->declare_symbol(name, value);
+  if (MC_is_active())
+    AppSide::get()->declare_symbol(name, value);
 #endif
 }
 
 void MC_ignore(void* addr, size_t size)
 {
-#if SIMGRID_HAVE_MC
-  xbt_assert(simgrid::mc::model_checking_mode != simgrid::mc::ModelCheckingMode::CHECKER_SIDE,
+#if SIMGRID_HAVE_STATEFUL_MC
+  xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
              "This should be called from the client side");
-  simgrid::mc::AppSide::get()->ignore_memory(addr, size);
+  if (MC_is_active())
+    AppSide::get()->ignore_memory(addr, size);
 #endif
 }
 
 void MC_ignore_heap(void *address, size_t size)
 {
-#if SIMGRID_HAVE_MC
-  xbt_assert(simgrid::mc::model_checking_mode != simgrid::mc::ModelCheckingMode::CHECKER_SIDE,
+#if SIMGRID_HAVE_STATEFUL_MC
+  xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
              "This should be called from the client side");
-  simgrid::mc::AppSide::get()->ignore_heap(address, size);
+  if (MC_is_active())
+    AppSide::get()->ignore_heap(address, size);
 #endif
 }
 
 void MC_unignore_heap(void* address, size_t size)
 {
-#if SIMGRID_HAVE_MC
-  xbt_assert(simgrid::mc::model_checking_mode != simgrid::mc::ModelCheckingMode::CHECKER_SIDE,
+#if SIMGRID_HAVE_STATEFUL_MC
+  xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
              "This should be called from the client side");
-  simgrid::mc::AppSide::get()->unignore_heap(address, size);
+  if (MC_is_active())
+    AppSide::get()->unignore_heap(address, size);
 #endif
 }
index f219a11..571ec6e 100644 (file)
@@ -8,40 +8,45 @@
 #include "src/simgrid/sg_config.hpp"
 #include <simgrid/modelchecker.h>
 
-#if SIMGRID_HAVE_MC
+#if SIMGRID_HAVE_STATEFUL_MC
 #include <string_view>
 #endif
 
 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(xbt_cfg);
 
-simgrid::mc::ModelCheckingMode simgrid::mc::model_checking_mode = simgrid::mc::ModelCheckingMode::NONE;
+static simgrid::mc::ModelCheckingMode model_checking_mode = simgrid::mc::ModelCheckingMode::NONE;
+simgrid::mc::ModelCheckingMode simgrid::mc::get_model_checking_mode()
+{
+  return model_checking_mode;
+}
+void simgrid::mc::set_model_checking_mode(simgrid::mc::ModelCheckingMode mode)
+{
+  model_checking_mode = mode;
+}
 
 static void _mc_cfg_cb_check(const char* spec, bool more_check = true)
 {
-#if SIMGRID_HAVE_MC
   xbt_assert(_sg_cfg_init_status == 0 || MC_is_active() || MC_record_replay_is_active() || not more_check,
              "Specifying a %s is only allowed within the model-checker. Please use simgrid-mc, or specify this option "
              "after the replay path.",
              spec);
-#else
-  xbt_die("Specifying a %s is only allowed within the model-checker. Please enable it before the compilation.", spec);
-#endif
 }
 
 /* Replay (this part is enabled even if MC it disabled) */
 simgrid::config::Flag<std::string> _sg_mc_record_path{
     "model-check/replay", "Model-check path to replay (as reported by SimGrid when a violation is reported)", "",
     [](std::string_view value) {
-      xbt_assert(simgrid::mc::model_checking_mode == simgrid::mc::ModelCheckingMode::NONE ||
-                     simgrid::mc::model_checking_mode == simgrid::mc::ModelCheckingMode::REPLAY,
+      if (value.empty()) // Ignore default value
+        return;
+      xbt_assert(simgrid::mc::get_model_checking_mode() == simgrid::mc::ModelCheckingMode::NONE ||
+                     simgrid::mc::get_model_checking_mode() == simgrid::mc::ModelCheckingMode::REPLAY,
                  "Specifying a MC replay path is not allowed when running the model-checker in mode %s. "
                  "Either remove the model-check/replay parameter, or execute your code out of simgrid-mc.",
-                 to_c_str(simgrid::mc::model_checking_mode));
-      simgrid::mc::model_checking_mode = simgrid::mc::ModelCheckingMode::REPLAY;
+                 to_c_str(simgrid::mc::get_model_checking_mode()));
+      simgrid::mc::set_model_checking_mode(simgrid::mc::ModelCheckingMode::REPLAY);
       MC_record_path()                 = value;
     }};
 
-#if SIMGRID_HAVE_MC
 simgrid::config::Flag<bool> _sg_mc_timeout{
     "model-check/timeout", "Whether to enable timeouts for wait requests", false, [](bool) {
       _mc_cfg_cb_check("value to enable/disable timeout for wait requests", not MC_record_replay_is_active());
@@ -52,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{
@@ -61,12 +67,20 @@ simgrid::config::Flag<bool> _sg_mc_sleep_set{
     [](bool) { _mc_cfg_cb_check("value to enable/disable the use of sleep-set in the reduction algorithm"); }};
 
 simgrid::config::Flag<std::string> _sg_mc_strategy{
-    "model-check/strategy", "Specify the the kind of heuristic to use for guided model-checking", "none",
-    [](std::string_view value) {
-      if (value != "none" && value != "nb_wait")
-        xbt_die("configuration option 'model-check/guided-mc' can only take 'none' or 'nb_wait' as a value");
+    "model-check/strategy",
+    "Specify the the kind of heuristic to use for guided model-checking",
+    "none",
+    {{"none", "No specific strategy: simply pick the first available transistion and act as a DFS."},
+     {"max_match_comm", "Try to minimize the number of in-fly communication by appairing matching send and receive."},
+     {"min_match_comm", "Try to maximize the number of in-fly communication by not appairing matching send and receive."},
+     {"uniform", "No specific strategy: choices are made randomly based on a uniform sampling."}
     }};
 
+simgrid::config::Flag<int> _sg_mc_random_seed{"model-check/rand-seed",
+                                              "give a specific random seed to initialize the uniform distribution", 0,
+                                              [](int) { _mc_cfg_cb_check("Random seed"); }};
+
+#if SIMGRID_HAVE_STATEFUL_MC
 simgrid::config::Flag<int> _sg_mc_checkpoint{
     "model-check/checkpoint", "Specify the amount of steps between checkpoints during stateful model-checking "
                               "(default: 0 => stateless verification). If value=1, one checkpoint is saved for each "
@@ -99,6 +113,7 @@ simgrid::config::Flag<bool> _sg_mc_unfolding_checker{
     "Whether to enable the unfolding-based dynamic partial order reduction to MPI programs", false, [](bool) {
       _mc_cfg_cb_check("value to to enable/disable the unfolding-based dynamic partial order reduction to MPI programs");
     }};
+#endif
 
 simgrid::config::Flag<std::string> _sg_mc_buffering{
     "smpi/buffering",
@@ -128,13 +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;
   }
-  return cfg_mc_reduction.get() == "dpor";
-}
 
-#endif
+  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;
+  }
+}
index da11497..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);
-extern XBT_PUBLIC ModelCheckingMode model_checking_mode;
-};
+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;
@@ -23,6 +25,7 @@ extern XBT_PUBLIC simgrid::config::Flag<bool> _sg_mc_send_determinism;
 extern XBT_PUBLIC simgrid::config::Flag<bool> _sg_mc_unfolding_checker;
 extern XBT_PRIVATE simgrid::config::Flag<bool> _sg_mc_timeout;
 extern XBT_PRIVATE simgrid::config::Flag<int> _sg_mc_max_depth;
+extern XBT_PRIVATE simgrid::config::Flag<int> _sg_mc_random_seed;
 extern "C" XBT_PUBLIC int _sg_mc_max_visited_states;
 extern XBT_PRIVATE simgrid::config::Flag<std::string> _sg_mc_dot_output_file;
 extern XBT_PRIVATE simgrid::config::Flag<bool> _sg_mc_termination;
diff --git a/src/mc/mc_environ.h b/src/mc/mc_environ.h
new file mode 100644 (file)
index 0000000..a6e8979
--- /dev/null
@@ -0,0 +1,27 @@
+/* Copyright (c) 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 MC_ENVIRON_H
+#define MC_ENVIRON_H
+
+/* Define macros for the name of environment variables used by the MC.  Keep them in a separate file without any other
+ * includes, since it's also loaded from mmalloc.
+ */
+
+/** Environment variable name used to pass the communication socket.
+ *
+ * It is set by `simgrid-mc` to enable MC support in the children processes.
+ */
+#define MC_ENV_SOCKET_FD "SIMGRID_MC_SOCKET_FD"
+
+/** Environment variable name defined when ptrace(2) is used to control the MCed process.
+ */
+#define MC_ENV_NEED_PTRACE "SIMGRID_MC_NEED_PTRACE"
+
+/** Environment variable used to request additional system statistics.
+ */
+#define MC_ENV_SYSTEM_STATISTICS "SIMGRID_MC_SYSTEM_STATISTICS"
+
+#endif
index b5094ca..234ea14 100644 (file)
@@ -8,22 +8,22 @@
 #include "xbt/base.h"
 #include <exception>
 
-constexpr int SIMGRID_MC_EXIT_SUCCESS         = 0;
-constexpr int SIMGRID_MC_EXIT_SAFETY          = 1;
-constexpr int SIMGRID_MC_EXIT_LIVENESS        = 2;
-constexpr int SIMGRID_MC_EXIT_DEADLOCK        = 3;
-constexpr int SIMGRID_MC_EXIT_NON_TERMINATION = 4;
-constexpr int SIMGRID_MC_EXIT_NON_DETERMINISM = 5;
-constexpr int SIMGRID_MC_EXIT_PROGRAM_CRASH   = 6;
-
-constexpr int SIMGRID_MC_EXIT_ERROR           = 63;
-
 namespace simgrid::mc {
-class XBT_PUBLIC DeadlockError : public std::exception {
-};
-class XBT_PUBLIC TerminationError : public std::exception {
+
+enum class ExitStatus {
+  SUCCESS         = 0,
+  SAFETY          = 1,
+  LIVENESS        = 2,
+  DEADLOCK        = 3,
+  NON_TERMINATION = 4,
+  NON_DETERMINISM = 5,
+  PROGRAM_CRASH   = 6,
+  ERROR           = 63
 };
-class XBT_PUBLIC LivenessError : public std::exception {
+
+struct McError : public std::exception {
+  const ExitStatus value;
+  explicit McError(ExitStatus v = ExitStatus::ERROR) : value(v) {}
 };
 } // namespace simgrid::mc
 
index bc444ff..262fa72 100644 (file)
@@ -6,7 +6,7 @@
 #include "src/kernel/actor/ActorImpl.hpp"
 #include "src/mc/mc.h"
 
-#if SIMGRID_HAVE_MC
+#if SIMGRID_HAVE_STATEFUL_MC
 #include "src/mc/api/RemoteApp.hpp"
 #include "src/mc/explo/Exploration.hpp"
 #include "src/mc/inspect/mc_unw.hpp"
@@ -34,7 +34,7 @@ std::vector<double> processes_time;
 
 }
 
-#if SIMGRID_HAVE_MC
+#if SIMGRID_HAVE_STATEFUL_MC
 
 namespace simgrid::mc {
 
index 6f1d32f..63d9fc0 100644 (file)
 #include "src/mc/mc_replay.hpp"
 #include "src/mc/transition/Transition.hpp"
 
-#if SIMGRID_HAVE_MC
-#include "src/mc/api/State.hpp"
-#include "src/mc/explo/Exploration.hpp"
-#include "src/mc/mc_private.hpp"
-#endif
-
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_record, mc, "Logging specific to MC record/replay facility");
 
 namespace simgrid::mc {
@@ -57,7 +51,7 @@ void RecordTrace::replay() const
   const auto& actor_list = engine->get_actor_list();
   if (actor_list.empty()) {
     XBT_INFO("The replay of the trace is complete. The application is terminating.");
-  } else if (std::none_of(begin(actor_list), end(actor_list),
+  } else if (std::none_of(std::begin(actor_list), std::end(actor_list),
                           [](const auto& kv) { return mc::actor_is_enabled(kv.second); })) {
     XBT_INFO("The replay of the trace is complete. DEADLOCK detected.");
     engine->display_all_actor_status();
@@ -100,12 +94,12 @@ simgrid::mc::RecordTrace::RecordTrace(const char* data)
   }
 }
 
-#if SIMGRID_HAVE_MC
-
 std::string simgrid::mc::RecordTrace::to_string() const
 {
   std::ostringstream stream;
   for (auto i = transitions_.begin(); i != transitions_.end(); ++i) {
+    if (*i == nullptr)
+      continue;
     if (i != transitions_.begin())
       stream << ';';
     stream << (*i)->aid_;
@@ -114,7 +108,4 @@ std::string simgrid::mc::RecordTrace::to_string() const
   }
   return stream.str();
 }
-
-#endif
-
 } // namespace simgrid::mc
index 2958780..a2f7310 100644 (file)
 #include "src/mc/mc_forward.hpp"
 #include "xbt/base.h"
 
+#include <deque>
 #include <string>
-#include <vector>
 
 namespace simgrid::mc {
 
 class RecordTrace {
-  std::vector<Transition*> transitions_;
+  std::deque<Transition*> transitions_;
 
 public:
-  RecordTrace() = default;
+  // Use rule-of-three, and implicitely disable the move constructor which cannot be 'noexcept' (as required by C++ Core
+  // Guidelines), due to the std::deque member.
+  RecordTrace()                   = default;
+  RecordTrace(const RecordTrace&) = default;
+  ~RecordTrace()                  = default;
 
   /** Build a trace that can be replayed from a string representation */
   explicit RecordTrace(const char* data);
   /** Make a string representation that can later be used to create a new trace */
   std::string to_string() const;
 
+  void push_front(Transition* t) { transitions_.push_front(t); }
   void push_back(Transition* t) { transitions_.push_back(t); }
+  std::deque<Transition*>::const_iterator begin() const { return transitions_.begin(); }
+  std::deque<Transition*>::const_iterator end() const { return transitions_.end(); }
 
   /** Replay all transitions of a trace */
   void replay() const;
index a5d5ffa..39326a0 100644 (file)
 #include "src/kernel/actor/SimcallObserver.hpp"
 #include "src/mc/mc_base.hpp"
 #include "src/mc/mc_config.hpp"
+#include "src/mc/mc_environ.h"
+#if SIMGRID_HAVE_STATEFUL_MC
 #include "src/mc/sosp/RemoteProcessMemory.hpp"
+#endif
 #if HAVE_SMPI
 #include "src/smpi/include/private.hpp"
 #endif
@@ -21,6 +24,7 @@
 #include <simgrid/modelchecker.h>
 
 #include <cerrno>
+#include <cinttypes>
 #include <cstdio> // setvbuf
 #include <cstdlib>
 #include <memory>
@@ -38,16 +42,18 @@ namespace simgrid::mc {
 
 std::unique_ptr<AppSide> AppSide::instance_;
 
-AppSide* AppSide::initialize()
+AppSide* AppSide::get()
 {
-  if (not std::getenv(MC_ENV_SOCKET_FD)) // We are not in MC mode: don't initialize the MC world
+  // Only initialize the MC world once
+  if (instance_ != nullptr)
+    return instance_.get();
+
+  if (std::getenv(MC_ENV_SOCKET_FD) == nullptr) // We are not in MC mode: don't initialize the MC world
     return nullptr;
 
-  // Do not break if we are called multiple times:
-  if (instance_)
-    return instance_.get();
+  XBT_DEBUG("Initialize the MC world. %s=%s", MC_ENV_NEED_PTRACE, std::getenv(MC_ENV_NEED_PTRACE));
 
-  simgrid::mc::model_checking_mode = ModelCheckingMode::APP_SIDE;
+  simgrid::mc::set_model_checking_mode(ModelCheckingMode::APP_SIDE);
 
   setvbuf(stdout, nullptr, _IOLBF, 0);
 
@@ -56,17 +62,10 @@ AppSide* AppSide::initialize()
   int fd             = xbt_str_parse_int(fd_env, "Not a number in variable '" MC_ENV_SOCKET_FD "'");
   XBT_DEBUG("Model-checked application found socket FD %i", fd);
 
-  // Check the socket type/validity:
-  int type;
-  socklen_t socklen = sizeof(type);
-  xbt_assert(getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &socklen) == 0, "Could not check socket type");
-  xbt_assert(type == SOCK_SEQPACKET, "Unexpected socket type %i", type);
-  XBT_DEBUG("Model-checked application found expected socket type");
-
   instance_ = std::make_unique<simgrid::mc::AppSide>(fd);
 
   // Wait for the model-checker:
-  if (getenv("MC_NEED_PTRACE") != nullptr) {
+  if (getenv(MC_ENV_NEED_PTRACE) != nullptr) {
     errno = 0;
 #if defined __linux__
     ptrace(PTRACE_TRACEME, 0, nullptr, nullptr);
@@ -102,7 +101,7 @@ void AppSide::handle_deadlock_check(const s_mc_message_t*) const
   s_mc_message_int_t answer = {};
   answer.type  = MessageType::DEADLOCK_CHECK_REPLY;
   answer.value = deadlock;
-  xbt_assert(channel_.send(answer) == 0, "Could not send response");
+  xbt_assert(channel_.send(answer) == 0, "Could not send response: %s", strerror(errno));
 }
 void AppSide::handle_simcall_execute(const s_mc_message_simcall_execute_t* message) const
 {
@@ -112,7 +111,8 @@ void AppSide::handle_simcall_execute(const s_mc_message_simcall_execute_t* messa
   // The client may send some messages to the server while processing the transition
   actor->simcall_handle(message->times_considered_);
   // Say the server that the transition is over and that it should proceed
-  xbt_assert(channel_.send(MessageType::WAITING) == 0, "Could not send MESSAGE_WAITING to model-checker");
+  xbt_assert(channel_.send(MessageType::WAITING) == 0, "Could not send MESSAGE_WAITING to model-checker: %s",
+             strerror(errno));
 
   // Finish the RPC from the server: return a serialized observer, to build a Transition on Checker side
   s_mc_message_simcall_execute_answer_t answer = {};
@@ -130,7 +130,7 @@ void AppSide::handle_simcall_execute(const s_mc_message_simcall_execute_t* messa
   answer.buffer.back() = '\0';
 
   XBT_DEBUG("send SIMCALL_EXECUTE_ANSWER(%s) ~> '%s'", actor->get_cname(), str.c_str());
-  xbt_assert(channel_.send(answer) == 0, "Could not send response");
+  xbt_assert(channel_.send(answer) == 0, "Could not send response: %s", strerror(errno));
 }
 
 void AppSide::handle_finalize(const s_mc_message_int_t* msg) const
@@ -147,26 +147,37 @@ void AppSide::handle_finalize(const s_mc_message_int_t* msg) const
 #endif
   }
   coverage_checkpoint();
-  xbt_assert(channel_.send(MessageType::FINALIZE_REPLY) == 0, "Could not answer to FINALIZE");
+  xbt_assert(channel_.send(MessageType::FINALIZE_REPLY) == 0, "Could not answer to FINALIZE: %s", strerror(errno));
   std::fflush(stdout);
   if (terminate_asap)
     ::_Exit(0);
 }
-void AppSide::handle_fork(const s_mc_message_int_t* msg)
+void AppSide::handle_fork(const s_mc_message_fork_t* msg)
 {
-  int pid = fork();
+  int status;
+  int pid;
+  /* Reap any zombie child, saving its status for later use in AppSide::handle_wait_child() */
+  while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+    child_statuses_[pid] = status;
+
+  pid = fork();
   xbt_assert(pid >= 0, "Could not fork application sub-process: %s.", strerror(errno));
 
   if (pid == 0) { // Child
-    int sock = socket(AF_LOCAL, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+    int sock = socket(AF_UNIX,
+#ifdef __APPLE__
+                      SOCK_STREAM, /* Mac OSX does not have AF_UNIX + SOCK_SEQPACKET, even if that's faster*/
+#else
+                      SOCK_SEQPACKET,
+#endif
+                      0);
 
     struct sockaddr_un addr = {};
-    addr.sun_family         = AF_LOCAL;
-    snprintf(addr.sun_path, 64, "/tmp/simgrid-mc-%lu", msg->value);
-    auto addr_size = offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path);
+    addr.sun_family         = AF_UNIX;
+    std::copy_n(begin(msg->socket_name), MC_SOCKET_NAME_LEN, addr.sun_path);
 
-    xbt_assert(connect(sock, (struct sockaddr*)&addr, addr_size) >= 0,
-               "Cannot connect to Checker on /tmp/simgrid-mc-%lu: %s.", msg->value, strerror(errno));
+    xbt_assert(connect(sock, (struct sockaddr*)&addr, sizeof addr) >= 0, "Cannot connect to Checker on %c%s: %s.",
+               (addr.sun_path[0] ? addr.sun_path[0] : '@'), addr.sun_path + 1, strerror(errno));
 
     channel_.reset_socket(sock);
 
@@ -174,13 +185,20 @@ void AppSide::handle_fork(const s_mc_message_int_t* msg)
     answer.type               = MessageType::FORK_REPLY;
     answer.value              = getpid();
     xbt_assert(channel_.send(answer) == 0, "Could not send response to WAIT_CHILD_REPLY: %s", strerror(errno));
+  } else {
+    XBT_VERB("App %d forks subprocess %d.", getpid(), pid);
   }
 }
 void AppSide::handle_wait_child(const s_mc_message_int_t* msg)
 {
   int status;
   errno = 0;
-  waitpid(msg->value, &status, 0);
+  if (auto search = child_statuses_.find(msg->value); search != child_statuses_.end()) {
+    status = search->second;
+    child_statuses_.erase(search); // We only need this info once
+  } else {
+    waitpid(msg->value, &status, 0);
+  }
   xbt_assert(errno == 0, "Cannot wait on behalf of the checker: %s.", strerror(errno));
 
   s_mc_message_int_t answer = {};
@@ -190,11 +208,15 @@ void AppSide::handle_wait_child(const s_mc_message_int_t* msg)
 }
 void AppSide::handle_need_meminfo()
 {
+#if SIMGRID_HAVE_STATEFUL_MC
   this->need_memory_info_                  = true;
   s_mc_message_need_meminfo_reply_t answer = {};
   answer.type                              = MessageType::NEED_MEMINFO_REPLY;
   answer.mmalloc_default_mdp               = mmalloc_get_current_heap();
-  xbt_assert(channel_.send(answer) == 0, "Could not send response to the request for meminfo.");
+  xbt_assert(channel_.send(answer) == 0, "Could not send response to the request for meminfo: %s", strerror(errno));
+#else
+  xbt_die("SimGrid was compiled without MC suppport, so liveness and similar features are not available.");
+#endif
 }
 void AppSide::handle_actors_status() const
 {
@@ -215,18 +237,15 @@ void AppSide::handle_actors_status() const
   answer.type                                       = MessageType::ACTORS_STATUS_REPLY_COUNT;
   answer.count                                      = static_cast<int>(status.size());
 
-  xbt_assert(channel_.send(answer) == 0, "Could not send ACTORS_STATUS_REPLY msg");
+  xbt_assert(channel_.send(answer) == 0, "Could not send ACTORS_STATUS_REPLY msg: %s", strerror(errno));
   if (answer.count > 0) {
     size_t size = status.size() * sizeof(s_mc_message_actors_status_one_t);
-    xbt_assert(channel_.send(status.data(), size) == 0, "Could not send ACTORS_STATUS_REPLY data");
+    xbt_assert(channel_.send(status.data(), size) == 0, "Could not send ACTORS_STATUS_REPLY data: %s", strerror(errno));
   }
 
   // Serialize each transition to describe what each actor is doing
   XBT_DEBUG("Deliver ACTOR_TRANSITION_PROBE payload");
   for (const auto& actor_status : status) {
-    if (not actor_status.enabled)
-      continue;
-
     const auto& actor        = actor_list.at(actor_status.aid);
     const int max_considered = actor_status.max_considered;
 
@@ -248,7 +267,7 @@ void AppSide::handle_actors_status() const
       strncpy(probe.buffer.data(), str.c_str(), probe.buffer.size() - 1);
       probe.buffer.back() = '\0';
 
-      xbt_assert(channel_.send(probe) == 0, "Could not send ACTOR_TRANSITION_PROBE payload");
+      xbt_assert(channel_.send(probe) == 0, "Could not send ACTOR_TRANSITION_PROBE payload: %s", strerror(errno));
     }
     // NOTE: We do NOT need to reset `times_considered` for each actor's
     // simcall observer here to the "original" value (i.e. the value BEFORE
@@ -262,7 +281,7 @@ void AppSide::handle_actors_maxpid() const
   s_mc_message_int_t answer = {};
   answer.type               = MessageType::ACTORS_MAXPID_REPLY;
   answer.value              = kernel::actor::ActorImpl::get_maxpid();
-  xbt_assert(channel_.send(answer) == 0, "Could not send response");
+  xbt_assert(channel_.send(answer) == 0, "Could not send response: %s", strerror(errno));
 }
 
 #define assert_msg_size(_name_, _type_)                                                                                \
@@ -281,7 +300,7 @@ void AppSide::handle_messages()
       XBT_DEBUG("Socket closed on the Checker side, bailing out.");
       ::_Exit(0); // Nobody's listening to that process anymore => exit as quickly as possible.
     }
-    xbt_assert(received_size >= 0, "Could not receive commands from the model-checker");
+    xbt_assert(received_size >= 0, "Could not receive commands from the model-checker: %s", strerror(errno));
     xbt_assert(static_cast<size_t>(received_size) >= sizeof(s_mc_message_t), "Cannot handle short message (size=%zd)",
                received_size);
 
@@ -307,8 +326,8 @@ void AppSide::handle_messages()
         break;
 
       case MessageType::FORK:
-        assert_msg_size("FORK", s_mc_message_int_t);
-        handle_fork((s_mc_message_int_t*)message_buffer.data());
+        assert_msg_size("FORK", s_mc_message_fork_t);
+        handle_fork((s_mc_message_fork_t*)message_buffer.data());
         break;
 
       case MessageType::WAIT_CHILD:
@@ -349,14 +368,16 @@ void AppSide::main_loop()
   sthread_enable();
   while (true) {
     simgrid::mc::execute_actors();
-    xbt_assert(channel_.send(MessageType::WAITING) == 0, "Could not send WAITING message to model-checker");
+    xbt_assert(channel_.send(MessageType::WAITING) == 0, "Could not send WAITING message to model-checker: %s",
+               strerror(errno));
     this->handle_messages();
   }
 }
 
 void AppSide::report_assertion_failure()
 {
-  xbt_assert(channel_.send(MessageType::ASSERTION_FAILED) == 0, "Could not send assertion to model-checker");
+  xbt_assert(channel_.send(MessageType::ASSERTION_FAILED) == 0, "Could not send assertion to model-checker: %s",
+             strerror(errno));
   this->handle_messages();
 }
 
@@ -365,11 +386,15 @@ void AppSide::ignore_memory(void* addr, std::size_t size) const
   if (not MC_is_active() || not need_memory_info_)
     return;
 
+#if SIMGRID_HAVE_STATEFUL_MC
   s_mc_message_ignore_memory_t message = {};
   message.type = MessageType::IGNORE_MEMORY;
   message.addr = (std::uintptr_t)addr;
   message.size = size;
-  xbt_assert(channel_.send(message) == 0, "Could not send IGNORE_MEMORY message to model-checker");
+  xbt_assert(channel_.send(message) == 0, "Could not send IGNORE_MEMORY message to model-checker: %s", strerror(errno));
+#else
+  xbt_die("Cannot really call ignore_heap() in non-SIMGRID_MC mode.");
+#endif
 }
 
 void AppSide::ignore_heap(void* address, std::size_t size) const
@@ -377,6 +402,7 @@ void AppSide::ignore_heap(void* address, std::size_t size) const
   if (not MC_is_active() || not need_memory_info_)
     return;
 
+#if SIMGRID_HAVE_STATEFUL_MC
   const s_xbt_mheap_t* heap = mmalloc_get_current_heap();
 
   s_mc_message_ignore_heap_t message = {};
@@ -392,7 +418,10 @@ void AppSide::ignore_heap(void* address, std::size_t size) const
     heap->heapinfo[message.block].busy_frag.ignore[message.fragment]++;
   }
 
-  xbt_assert(channel_.send(message) == 0, "Could not send ignored region to MCer");
+  xbt_assert(channel_.send(message) == 0, "Could not send ignored region to MCer: %s", strerror(errno));
+#else
+  xbt_die("Cannot really call ignore_heap() in non-SIMGRID_MC mode.");
+#endif
 }
 
 void AppSide::unignore_heap(void* address, std::size_t size) const
@@ -400,11 +429,15 @@ void AppSide::unignore_heap(void* address, std::size_t size) const
   if (not MC_is_active() || not need_memory_info_)
     return;
 
+#if SIMGRID_HAVE_STATEFUL_MC
   s_mc_message_ignore_memory_t message = {};
   message.type = MessageType::UNIGNORE_HEAP;
   message.addr = (std::uintptr_t)address;
   message.size = size;
-  xbt_assert(channel_.send(message) == 0, "Could not send IGNORE_HEAP message to model-checker");
+  xbt_assert(channel_.send(message) == 0, "Could not send IGNORE_HEAP message to model-checker: %s", strerror(errno));
+#else
+  xbt_die("Cannot really call unignore_heap() in non-SIMGRID_MC mode.");
+#endif
 }
 
 void AppSide::declare_symbol(const char* name, int* value) const
@@ -414,13 +447,17 @@ void AppSide::declare_symbol(const char* name, int* value) const
     return;
   }
 
+#if SIMGRID_HAVE_STATEFUL_MC
   s_mc_message_register_symbol_t message = {};
   message.type = MessageType::REGISTER_SYMBOL;
   xbt_assert(strlen(name) + 1 <= message.name.size(), "Symbol is too long");
   strncpy(message.name.data(), name, message.name.size() - 1);
   message.callback = nullptr;
   message.data     = value;
-  xbt_assert(channel_.send(message) == 0, "Could send REGISTER_SYMBOL message to model-checker");
+  xbt_assert(channel_.send(message) == 0, "Could send REGISTER_SYMBOL message to model-checker: %s", strerror(errno));
+#else
+  xbt_die("Cannot really call declare_symbol() in non-SIMGRID_MC mode.");
+#endif
 }
 
 /** Register a stack in the model checker
@@ -429,11 +466,13 @@ void AppSide::declare_symbol(const char* name, int* value) const
  *  when we analyze/compare the content of the heap so it must be told where
  *  they are with this function.
  */
+#if HAVE_UCONTEXT_H /* Apple don't want us to use ucontexts */
 void AppSide::declare_stack(void* stack, size_t size, ucontext_t* context) const
 {
   if (not MC_is_active() || not need_memory_info_)
     return;
 
+#if SIMGRID_HAVE_STATEFUL_MC
   const s_xbt_mheap_t* heap = mmalloc_get_current_heap();
 
   s_stack_region_t region = {};
@@ -445,6 +484,11 @@ void AppSide::declare_stack(void* stack, size_t size, ucontext_t* context) const
   s_mc_message_stack_region_t message = {};
   message.type         = MessageType::STACK_REGION;
   message.stack_region = region;
-  xbt_assert(channel_.send(message) == 0, "Could not send STACK_REGION to model-checker");
+  xbt_assert(channel_.send(message) == 0, "Could not send STACK_REGION to model-checker: %s", strerror(errno));
+#else
+  xbt_die("Cannot really call declare_stack() in non-SIMGRID_MC mode.");
+#endif
 }
+#endif
+
 } // namespace simgrid::mc
index 5dcb934..3941ffa 100644 (file)
@@ -23,6 +23,7 @@ private:
   Channel channel_;
   static std::unique_ptr<AppSide> instance_;
   bool need_memory_info_ = false; /* by default we don't send memory info, unless we got a NEED_MEMINFO */
+  std::unordered_map<int, int> child_statuses_;
 
 public:
   AppSide();
@@ -33,7 +34,7 @@ private:
   void handle_deadlock_check(const s_mc_message_t* msg) const;
   void handle_simcall_execute(const s_mc_message_simcall_execute_t* message) const;
   void handle_finalize(const s_mc_message_int_t* msg) const;
-  void handle_fork(const s_mc_message_int_t* msg);
+  void handle_fork(const s_mc_message_fork_t* msg);
   void handle_wait_child(const s_mc_message_int_t* msg);
   void handle_need_meminfo();
   void handle_actors_status() const;
@@ -52,10 +53,8 @@ public:
   void declare_stack(void* stack, size_t size, ucontext_t* context) const;
 #endif
 
-  // Singleton :/
   // TODO, remove the singleton antipattern.
-  static AppSide* initialize();
-  static AppSide* get() { return instance_.get(); }
+  static AppSide* get();
 };
 } // namespace simgrid::mc
 
index 6160412..63a6393 100644 (file)
@@ -1,5 +1,4 @@
-/* Copyright (c) 2015-2023. The SimGrid Team.
- * All rights reserved.                                                     */
+/* 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 <sys/types.h>
 #include <unistd.h>
 
-XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_Channel, mc, "MC interprocess communication");
+XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_channel, mc, "MC interprocess communication");
 
 namespace simgrid::mc {
+Channel::Channel(int sock, Channel const& other) : socket_(sock), buffer_(other.buffer_)
+{
+  XBT_DEBUG("Adopt %zu bytes buffered by father channel.", buffer_.size());
+}
 
 Channel::~Channel()
 {
@@ -43,16 +46,44 @@ int Channel::send(const void* message, size_t size) const
   return 0;
 }
 
-ssize_t Channel::receive(void* message, size_t size) const
+ssize_t Channel::receive(void* message, size_t size, int flags)
 {
-  ssize_t res = recv(this->socket_, message, size, 0);
-  xbt_assert(res != -1, "Channel::receive failure: %s", strerror(errno));
+  size_t bufsize = buffer_.size();
+  ssize_t copied = 0;
+  auto* whereto  = static_cast<char*>(message);
+  size_t todo    = size;
+  if (bufsize > 0) {
+    XBT_DEBUG("%d %zu bytes (of %zu expected) are already in buffer", getpid(), bufsize, size);
+    copied = std::min(size, bufsize);
+    std::copy_n(begin(buffer_), copied, whereto);
+    buffer_.erase(begin(buffer_), begin(buffer_) + copied);
+    todo -= copied;
+    whereto += copied;
+  }
+  ssize_t res = 0;
+  if (todo > 0) {
+    errno = 0;
+    res   = recv(this->socket_, whereto, todo, flags);
+    xbt_assert(res != -1 || errno == EAGAIN, "Channel::receive failure: %s", strerror(errno));
+    if (res == -1) {
+      res = 0;
+    }
+  }
+  XBT_DEBUG("%d Wanted %zu; Got %zd from buffer and %zd from network", getpid(), size, copied, res);
+  res += copied;
   if (static_cast<size_t>(res) >= sizeof(int) && is_valid_MessageType(*static_cast<const int*>(message))) {
-    XBT_DEBUG("Receive %s (requested %zu; received %zd at %p)", to_c_str(*static_cast<const MessageType*>(message)),
-              size, res, message);
+    XBT_DEBUG("%d Receive %s (requested %zu; received %zd at %p)", getpid(),
+              to_c_str(*static_cast<const MessageType*>(message)), size, res, message);
   } else {
     XBT_DEBUG("Receive %zd bytes", res);
   }
   return res;
 }
+
+void Channel::reinject(const char* data, size_t size)
+{
+  xbt_assert(size > 0, "Cannot reinject less than one char (size: %lu)", size);
+  XBT_DEBUG("%d Reinject %zu bytes on top of %zu pre-existing bytes", getpid(), size, buffer_.size());
+  buffer_.insert(end(buffer_), data, data + size);
+}
 } // namespace simgrid::mc
index c9e1aeb..06d22b1 100644 (file)
@@ -20,10 +20,12 @@ namespace simgrid::mc {
 class Channel {
   int socket_ = -1;
   template <class M> static constexpr bool messageType() { return std::is_class_v<M> && std::is_trivial_v<M>; }
+  std::vector<char> buffer_;
 
 public:
   Channel() = default;
   explicit Channel(int sock) : socket_(sock) {}
+  Channel(int sock, Channel const& other);
   ~Channel();
 
   // No copy:
@@ -44,11 +46,13 @@ public:
   }
 
   // Receive
-  ssize_t receive(void* message, size_t size) const;
-  template <class M> typename std::enable_if_t<messageType<M>(), ssize_t> receive(M& m) const
+  ssize_t receive(void* message, size_t size, int flags = 0);
+  template <class M> typename std::enable_if_t<messageType<M>(), ssize_t> receive(M& m)
   {
-    return this->receive(&m, sizeof(M));
+    return this->receive(&m, sizeof(M), 0);
   }
+  void reinject(const char* data, size_t size);
+  bool has_pending_data() const { return not buffer_.empty(); }
 
   // Socket handling
   int get_socket() const { return socket_; }
index a19f093..211995b 100644 (file)
@@ -5,9 +5,14 @@
 
 #include "src/mc/remote/CheckerSide.hpp"
 #include "src/mc/explo/Exploration.hpp"
+#include "src/mc/mc_environ.h"
+#include "xbt/config.hpp"
+#include "xbt/system_error.hpp"
+
+#if SIMGRID_HAVE_STATEFUL_MC
 #include "src/mc/explo/LivenessChecker.hpp"
 #include "src/mc/sosp/RemoteProcessMemory.hpp"
-#include "xbt/system_error.hpp"
+#endif
 
 #ifdef __linux__
 #include <sys/prctl.h>
@@ -50,14 +55,9 @@ XBT_ATTRIB_NORETURN static void run_child_process(int socket, const std::vector<
   xbt_assert(prctl(PR_SET_PDEATHSIG, SIGHUP) == 0, "Could not PR_SET_PDEATHSIG");
 #endif
 
-  // Remove CLOEXEC to pass the socket to the application
-  int fdflags = fcntl(socket, F_GETFD, 0);
-  xbt_assert(fdflags != -1 && fcntl(socket, F_SETFD, fdflags & ~FD_CLOEXEC) != -1,
-             "Could not remove CLOEXEC for socket");
-
   setenv(MC_ENV_SOCKET_FD, std::to_string(socket).c_str(), 1);
   if (need_ptrace)
-    setenv("MC_NEED_PTRACE", "1", 1);
+    setenv(MC_ENV_NEED_PTRACE, "1", 1);
 
   /* Setup the tokenizer that parses the cfg:model-check/setenv parameter */
   using Tokenizer = boost::tokenizer<boost::char_separator<char>>;
@@ -110,13 +110,14 @@ static void wait_application_process(pid_t pid)
 #elif defined BSD
   ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
 #else
-#error "no ptrace equivalent coded for this platform"
+  xbt_die("no ptrace equivalent coded for this platform, stateful model-checking is impossible.");
 #endif
   xbt_assert(errno == 0,
              "Ptrace does not seem to be usable in your setup (errno: %d). "
              "If you run from within a docker, adding `--cap-add SYS_PTRACE` to the docker line may help. "
              "If it does not help, please report this bug.",
              errno);
+  XBT_DEBUG("%d ptrace correctly setup.", getpid());
 }
 
 void CheckerSide::setup_events(bool socket_only)
@@ -129,18 +130,22 @@ void CheckerSide::setup_events(bool socket_only)
       [](evutil_socket_t, short events, void* arg) {
         auto checker = static_cast<simgrid::mc::CheckerSide*>(arg);
         if (events == EV_READ) {
-          std::array<char, MC_MESSAGE_LENGTH> buffer;
-          ssize_t size = recv(checker->get_channel().get_socket(), buffer.data(), buffer.size(), MSG_DONTWAIT);
-          if (size == -1) {
-            XBT_ERROR("Channel::receive failure: %s", strerror(errno));
-            if (errno != EAGAIN)
-              throw simgrid::xbt::errno_error();
-          }
-
-          if (size == 0) // The app closed the socket. It must be dead by now.
-            checker->handle_waitpid();
-          else if (not checker->handle_message(buffer.data(), size))
-            checker->break_loop();
+          do {
+            std::array<char, MC_MESSAGE_LENGTH> buffer;
+            ssize_t size = checker->get_channel().receive(buffer.data(), buffer.size(), MSG_DONTWAIT);
+            if (size == -1) {
+              XBT_ERROR("Channel::receive failure: %s", strerror(errno));
+              if (errno != EAGAIN)
+                throw simgrid::xbt::errno_error();
+            }
+
+            if (size == 0) // The app closed the socket. It must be dead by now.
+              checker->handle_waitpid();
+            else if (not checker->handle_message(buffer.data(), size)) {
+              checker->break_loop();
+              break;
+            }
+          } while (checker->get_channel().has_pending_data());
         } else {
           xbt_die("Unexpected event");
         }
@@ -172,11 +177,19 @@ void CheckerSide::setup_events(bool socket_only)
 /* When this constructor is called, no other checkerside exists */
 CheckerSide::CheckerSide(const std::vector<char*>& args, bool need_memory_info) : running_(true)
 {
-  // Create an AF_LOCAL socketpair used for exchanging messages between the model-checker process (ancestor)
+  XBT_DEBUG("Create a CheckerSide. Needs_meminfo: %s", need_memory_info ? "YES" : "no");
+
+  // Create an AF_UNIX socketpair used for exchanging messages between the model-checker process (ancestor)
   // and the application process (child)
   int sockets[2];
-  xbt_assert(socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != -1, "Could not create socketpair: %s",
-             strerror(errno));
+  xbt_assert(socketpair(AF_UNIX,
+#ifdef __APPLE__
+                        SOCK_STREAM, /* Mac OSX does not have AF_UNIX + SOCK_SEQPACKET, even if that's faster */
+#else
+                        SOCK_SEQPACKET,
+#endif
+                        0, sockets) != -1,
+             "Could not create socketpair: %s", strerror(errno));
 
   pid_ = fork();
   xbt_assert(pid_ >= 0, "Could not fork application process");
@@ -193,6 +206,7 @@ CheckerSide::CheckerSide(const std::vector<char*>& args, bool need_memory_info)
 
   setup_events(false); /* we need a signal handler too */
   if (need_memory_info) {
+#if SIMGRID_HAVE_STATEFUL_MC
     // setup ptrace and sync with the app
     wait_application_process(pid_);
 
@@ -208,6 +222,9 @@ CheckerSide::CheckerSide(const std::vector<char*>& args, bool need_memory_info)
 
     /* We now have enough info to create the memory address space */
     remote_memory_ = std::make_unique<simgrid::mc::RemoteProcessMemory>(pid_, answer.mmalloc_default_mdp);
+#else
+    xbt_die("Cannot introspect memory without MC support");
+#endif
   }
 
   wait_for_requests();
@@ -225,7 +242,7 @@ CheckerSide::~CheckerSide()
 
 /* This constructor is called when cloning a checkerside to get its application to fork away */
 CheckerSide::CheckerSide(int socket, CheckerSide* child_checker)
-    : channel_(socket), running_(true), child_checker_(child_checker)
+    : channel_(socket, child_checker->channel_), running_(true), child_checker_(child_checker)
 {
   setup_events(true); // We already have a signal handled in that case
 
@@ -241,11 +258,12 @@ CheckerSide::CheckerSide(int socket, CheckerSide* child_checker)
   wait_for_requests();
 }
 
-std::unique_ptr<CheckerSide> CheckerSide::clone(int master_socket)
+std::unique_ptr<CheckerSide> CheckerSide::clone(int master_socket, const std::string& master_socket_name)
 {
-  s_mc_message_int_t m = {};
-  m.type               = MessageType::FORK;
-  m.value              = getpid();
+  s_mc_message_fork_t m = {};
+  m.type                = MessageType::FORK;
+  xbt_assert(master_socket_name.size() == MC_SOCKET_NAME_LEN);
+  std::copy_n(begin(master_socket_name), MC_SOCKET_NAME_LEN, begin(m.socket_name));
   xbt_assert(get_channel().send(m) == 0, "Could not ask the app to fork on need.");
 
   int sock = accept(master_socket, nullptr /* I know who's connecting*/, nullptr);
@@ -283,14 +301,17 @@ void CheckerSide::break_loop() const
 bool CheckerSide::handle_message(const char* buffer, ssize_t size)
 {
   s_mc_message_t base_message;
+  ssize_t consumed;
   xbt_assert(size >= (ssize_t)sizeof(base_message), "Broken message. Got only %ld bytes.", size);
   memcpy(&base_message, buffer, sizeof(base_message));
 
   switch (base_message.type) {
     case MessageType::IGNORE_HEAP: {
+      consumed = sizeof(s_mc_message_ignore_heap_t);
+#if SIMGRID_HAVE_STATEFUL_MC
       if (remote_memory_ != nullptr) {
         s_mc_message_ignore_heap_t message;
-        xbt_assert(size == sizeof(message), "Broken message");
+        xbt_assert(size >= static_cast<ssize_t>(sizeof(message)), "Broken message");
         memcpy(&message, buffer, sizeof(message));
 
         IgnoredHeapRegion region;
@@ -299,69 +320,94 @@ bool CheckerSide::handle_message(const char* buffer, ssize_t size)
         region.address  = message.address;
         region.size     = message.size;
         get_remote_memory()->ignore_heap(region);
-      } else {
+      } else
+#endif
         XBT_INFO("Ignoring a IGNORE_HEAP message because we don't need to introspect memory.");
-      }
       break;
     }
 
     case MessageType::UNIGNORE_HEAP: {
+      consumed = sizeof(s_mc_message_ignore_memory_t);
+#if SIMGRID_HAVE_STATEFUL_MC
       if (remote_memory_ != nullptr) {
         s_mc_message_ignore_memory_t message;
-        xbt_assert(size == sizeof(message), "Broken message");
+        xbt_assert(size == static_cast<ssize_t>(sizeof(message)), "Broken message");
         memcpy(&message, buffer, sizeof(message));
         get_remote_memory()->unignore_heap((void*)message.addr, message.size);
-      } else {
+      } else
+#endif
         XBT_INFO("Ignoring an UNIGNORE_HEAP message because we don't need to introspect memory.");
-      }
       break;
     }
 
     case MessageType::IGNORE_MEMORY: {
+      consumed = sizeof(s_mc_message_ignore_memory_t);
+#if SIMGRID_HAVE_STATEFUL_MC
       if (remote_memory_ != nullptr) {
         s_mc_message_ignore_memory_t message;
-        xbt_assert(size == sizeof(message), "Broken message");
+        xbt_assert(size >= static_cast<ssize_t>(sizeof(message)), "Broken message");
         memcpy(&message, buffer, sizeof(message));
         get_remote_memory()->ignore_region(message.addr, message.size);
-      } else {
+      } else
+#endif
         XBT_INFO("Ignoring an IGNORE_MEMORY message because we don't need to introspect memory.");
-      }
       break;
     }
 
     case MessageType::STACK_REGION: {
+      consumed = sizeof(s_mc_message_stack_region_t);
+#if SIMGRID_HAVE_STATEFUL_MC
       if (remote_memory_ != nullptr) {
         s_mc_message_stack_region_t message;
-        xbt_assert(size == sizeof(message), "Broken message");
+        xbt_assert(size >= static_cast<ssize_t>(sizeof(message)), "Broken message");
         memcpy(&message, buffer, sizeof(message));
         get_remote_memory()->stack_areas().push_back(message.stack_region);
-      } else {
+      } else
+#endif
         XBT_INFO("Ignoring an STACK_REGION message because we don't need to introspect memory.");
-      }
       break;
     }
 
     case MessageType::REGISTER_SYMBOL: {
+      consumed = sizeof(s_mc_message_register_symbol_t);
+#if SIMGRID_HAVE_STATEFUL_MC
       s_mc_message_register_symbol_t message;
-      xbt_assert(size == sizeof(message), "Broken message");
+      xbt_assert(size >= static_cast<ssize_t>(sizeof(message)), "Broken message");
       memcpy(&message, buffer, sizeof(message));
       xbt_assert(not message.callback, "Support for client-side function proposition is not implemented.");
       XBT_DEBUG("Received symbol: %s", message.name.data());
 
       LivenessChecker::automaton_register_symbol(*get_remote_memory(), message.name.data(), remote((int*)message.data));
+#else
+      xbt_die("Please don't use liveness properties when MC is compiled out.");
+#endif
       break;
     }
 
     case MessageType::WAITING:
+      consumed = sizeof(s_mc_message_t);
+      if (size > consumed) {
+        XBT_DEBUG("%d reinject %d bytes after a %s message", getpid(), (int)(size - consumed),
+                  to_c_str(base_message.type));
+        channel_.reinject(&buffer[consumed], size - consumed);
+      }
+
       return false;
 
     case MessageType::ASSERTION_FAILED:
+      // report_assertion_failure() is NORETURN, but it may change when we report more than one error per run,
+      // so please keep the consumed computation even if clang-static detects it as a dead affectation.
+      consumed = sizeof(s_mc_message_t);
       Exploration::get_instance()->report_assertion_failure();
       break;
 
     default:
       xbt_die("Unexpected message from the application");
   }
+  if (size > consumed) {
+    XBT_DEBUG("%d reinject %d bytes after a %s message", getpid(), (int)(size - consumed), to_c_str(base_message.type));
+    channel_.reinject(&buffer[consumed], size - consumed);
+  }
   return true;
 }
 
@@ -378,8 +424,10 @@ void CheckerSide::wait_for_requests()
 
 void CheckerSide::clear_memory_cache()
 {
+#if SIMGRID_HAVE_STATEFUL_MC
   if (remote_memory_)
     remote_memory_->clear_cache();
+#endif
 }
 
 void CheckerSide::handle_dead_child(int status)
@@ -420,7 +468,8 @@ void CheckerSide::handle_dead_child(int status)
 
 void CheckerSide::handle_waitpid()
 {
-  XBT_DEBUG("Check for wait event");
+  XBT_DEBUG("%d checks for wait event. %s", getpid(),
+            child_checker_ == nullptr ? "Wait directly." : "Ask our proxy to wait for its child.");
 
   if (child_checker_ == nullptr) { // Wait directly
     int status;
@@ -440,7 +489,6 @@ void CheckerSide::handle_waitpid()
     }
 
   } else { // Ask our proxy to wait for us
-
     s_mc_message_int_t request = {};
     request.type               = MessageType::WAIT_CHILD;
     request.value              = pid_;
index a3cb6a6..d89f712 100644 (file)
@@ -21,7 +21,9 @@ class CheckerSide {
   event* socket_event_;
   event* signal_event_;
   std::unique_ptr<event_base, decltype(&event_base_free)> base_{nullptr, &event_base_free};
+#if SIMGRID_HAVE_STATEFUL_MC
   std::unique_ptr<RemoteProcessMemory> remote_memory_;
+#endif
 
   Channel channel_;
   bool running_ = false;
@@ -55,7 +57,7 @@ public:
   void wait_for_requests();
 
   /* Create a new CheckerSide by forking the currently existing one, and connect it through the master_socket */
-  std::unique_ptr<CheckerSide> clone(int master_socket);
+  std::unique_ptr<CheckerSide> clone(int master_socket, const std::string& master_socket_name);
 
   /** Ask the application to run post-mortem analysis, and maybe to stop ASAP */
   void finalize(bool terminate_asap = false);
@@ -64,7 +66,9 @@ public:
   pid_t get_pid() const { return pid_; }
   bool running() const { return running_; }
   void terminate() { running_ = false; }
+#if SIMGRID_HAVE_STATEFUL_MC
   RemoteProcessMemory* get_remote_memory() { return remote_memory_.get(); }
+#endif
 };
 
 } // namespace simgrid::mc
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 920f43f..e4cac55 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <array>
 #include <cstdint>
+#include <sys/un.h>
 
 // ***** Messages
 namespace simgrid::mc {
@@ -32,11 +33,12 @@ XBT_DECLARE_ENUM_CLASS(MessageType, NONE, NEED_MEMINFO, NEED_MEMINFO_REPLY, FORK
 } // namespace simgrid::mc
 
 constexpr unsigned MC_MESSAGE_LENGTH                 = 512;
+constexpr unsigned MC_SOCKET_NAME_LEN                = sizeof(sockaddr_un::sun_path);
 constexpr unsigned SIMCALL_SERIALIZATION_BUFFER_SIZE = 2048;
 
 /** Basic structure for a MC message
  *
- *  The current version of the client/server protocol sends C structures over `AF_LOCAL`
+ *  The current version of the client/server protocol sends C structures over `AF_UNIX`
  *  `SOCK_SEQPACKET` sockets. This means that the protocol is ABI/architecture specific:
  *  we currently can't model-check a x86 process from a x86_64 process.
  *
@@ -87,6 +89,11 @@ struct s_mc_message_need_meminfo_reply_t {
   xbt_mheap_t mmalloc_default_mdp;
 };
 
+struct s_mc_message_fork_t {
+  simgrid::mc::MessageType type;
+  std::array<char, MC_SOCKET_NAME_LEN> socket_name;
+};
+
 struct s_mc_message_simcall_execute_t {
   simgrid::mc::MessageType type;
   aid_t aid_;
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 72e2355..ee28945 100644 (file)
@@ -219,7 +219,7 @@ Snapshot::Snapshot(long num_state, PageStore& store, RemoteProcessMemory& memory
   ignore_restore();
 }
 
-void Snapshot::add_region(RegionType type, RemoteProcessMemory& memory, ObjectInformation* object_info,
+void Snapshot::add_region(RegionType type, const RemoteProcessMemory& memory, ObjectInformation* object_info,
                           void* start_addr, std::size_t size)
 {
   if (type == RegionType::Data)
@@ -227,9 +227,9 @@ void Snapshot::add_region(RegionType type, RemoteProcessMemory& memory, ObjectIn
   else if (type == RegionType::Heap)
     xbt_assert(not object_info, "Unexpected object info for heap region.");
 
-  auto* region = new Region(page_store_, memory, type, start_addr, size);
+  auto region = std::make_unique<Region>(page_store_, memory, type, start_addr, size);
   region->object_info(object_info);
-  snapshot_regions_.push_back(std::unique_ptr<Region>(region));
+  snapshot_regions_.push_back(std::move(region));
 }
 
 void* Snapshot::read_bytes(void* buffer, std::size_t size, RemotePtr<void> address, ReadOptions options) const
index 04e871c..0b9fb57 100644 (file)
@@ -89,7 +89,7 @@ public:
   std::vector<s_mc_snapshot_ignored_data_t> ignored_data_;
 
 private:
-  void add_region(RegionType type, RemoteProcessMemory& memory, ObjectInformation* object_info, void* start_addr,
+  void add_region(RegionType type, const RemoteProcessMemory& memory, ObjectInformation* object_info, void* start_addr,
                   std::size_t size);
   void snapshot_regions(RemoteProcessMemory& memory);
   void snapshot_stacks(RemoteProcessMemory& memory);
index d2f7d0e..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;
-    Region* region0;
-    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 =
-      new 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 = new 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);
 
-  return {.size = byte_size, .src = source, .dstn = destination, .region0 = region0, .region = region};
+  return {.size    = byte_size,
+          .src     = source,
+          .dstn    = destination,
+          .region0 = std::move(region0),
+          .region  = std::move(region)};
 }
 
 void snap_test_helper::read_whole_region()
@@ -92,8 +89,6 @@ void snap_test_helper::read_whole_region()
 
     munmap(ret.dstn, ret.size);
     munmap(ret.src, ret.size);
-    delete ret.region0;
-    delete ret.region;
   }
 }
 
@@ -111,8 +106,6 @@ void snap_test_helper::read_region_parts()
     }
     munmap(ret.dstn, ret.size);
     munmap(ret.src, ret.size);
-    delete ret.region0;
-    delete ret.region;
   }
 }
 
@@ -122,12 +115,10 @@ void snap_test_helper::compare_whole_region()
     prologue_return ret = prologue(n);
 
     INFO("Unexpected match in MC_snapshot_region_memcmp() with previous snapshot");
-    REQUIRE(MC_snapshot_region_memcmp(ret.src, ret.region0, ret.src, ret.region, ret.size));
+    REQUIRE(MC_snapshot_region_memcmp(ret.src, ret.region0.get(), ret.src, ret.region.get(), ret.size));
 
     munmap(ret.dstn, ret.size);
     munmap(ret.src, ret.size);
-    delete ret.region0;
-    delete ret.region;
   }
 }
 
@@ -141,30 +132,26 @@ void snap_test_helper::compare_region_parts()
       size_t size   = simgrid::xbt::random::uniform_int(0, ret.size - offset - 1);
 
       INFO("Mismatch in MC_snapshot_region_memcmp()");
-      REQUIRE(not MC_snapshot_region_memcmp((char*)ret.src + offset, ret.region, (char*)ret.src + offset, ret.region,
-                                            size));
+      REQUIRE(not MC_snapshot_region_memcmp((char*)ret.src + offset, ret.region.get(), (char*)ret.src + offset,
+                                            ret.region.get(), size));
     }
     munmap(ret.dstn, ret.size);
     munmap(ret.src, ret.size);
-    delete ret.region0;
-    delete ret.region;
   }
 }
 
 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);
 
   munmap(ret.dstn, ret.size);
   munmap(ret.src, ret.size);
-  delete ret.region0;
-  delete ret.region;
 }
 
 /*************** End: class snap_test_helper *****************************/
@@ -173,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 49331de..463bfcb 100644 (file)
@@ -7,10 +7,6 @@
 #include "simgrid/config.h"
 #include "xbt/asserts.h"
 #include "xbt/string.hpp"
-#if SIMGRID_HAVE_MC
-#include "src/mc/api/RemoteApp.hpp"
-#include "src/mc/api/State.hpp"
-#endif
 
 #include <sstream>
 
@@ -31,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 a80a800..220b7cb 100644 (file)
@@ -7,10 +7,6 @@
 #include "simgrid/config.h"
 #include "xbt/asserts.h"
 #include "xbt/string.hpp"
-#if SIMGRID_HAVE_MC
-#include "src/mc/api/RemoteApp.hpp"
-#include "src/mc/api/State.hpp"
-#endif
 
 #include <sstream>
 
@@ -41,6 +37,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)
@@ -63,6 +63,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 de0ec2b..029548d 100644 (file)
@@ -5,13 +5,12 @@
 
 #include "src/mc/transition/TransitionComm.hpp"
 #include "simgrid/config.h"
-#include "xbt/asserts.h"
-#include "xbt/string.hpp"
-#if SIMGRID_HAVE_MC
 #include "src/mc/api/RemoteApp.hpp"
 #include "src/mc/api/State.hpp"
-#endif
+#include "xbt/asserts.h"
+#include "xbt/string.hpp"
 
+#include <inttypes.h>
 #include <sstream>
 
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_trans_comm, mc_transition,
@@ -43,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
@@ -81,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
 
@@ -113,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_;
 
@@ -165,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 b08de11..65077e9 100644 (file)
@@ -83,8 +83,7 @@ Here is an example of XML declaration:
 The different properties are:
 
 - ``battery_active``: Set the battery as active if set to 1 (default=0)
-- ``battery_power``: Set the initial power of the battery in W (default=0). A negative value indicates that the battery
-act as a load and charge. A positive value indicates that the battery act as a generator and discharge
+- ``battery_power``: Set the initial power of the battery in W (default=0). A negative value indicates that the battery act as a load and charge. A positive value indicates that the battery act as a generator and discharge
 - ``battery_state_of_charge``: Set the initial state of charge of the battery (default=0)
 - ``battery_state_of_charge_min``: Set the minimal state of charge of the battery (default=0.2)
 - ``battery_state_of_charge_max``: Set the maximal state of charge of the battery (default=0.8)
@@ -167,8 +166,8 @@ public:
   bool is_active() const;
   double get_power();
   double get_state_of_charge();
-  double get_state_of_charge_min();
-  double get_state_of_charge_max();
+  double get_state_of_charge_min() const;
+  double get_state_of_charge_max() const;
   double get_state_of_health();
   double get_capacity();
   double get_cumulative_cost();
@@ -322,12 +321,12 @@ double Battery::get_state_of_charge()
   return state_of_charge_;
 }
 
-double Battery::get_state_of_charge_min()
+double Battery::get_state_of_charge_min() const
 {
   return state_of_charge_min_;
 }
 
-double Battery::get_state_of_charge_max()
+double Battery::get_state_of_charge_max() const
 {
   return state_of_charge_max_;
 }
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 29b3700..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"
@@ -128,7 +129,7 @@ double LinkEnergy::get_power() const
 
   double power_slope = busy_ - idle_;
 
-  double normalized_link_usage = link_->get_usage() / link_->get_bandwidth();
+  double normalized_link_usage = link_->get_load() / link_->get_bandwidth();
   double dynamic_power         = power_slope * normalized_link_usage;
 
   return idle_ + dynamic_power;
@@ -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 481f1f2..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>
 
@@ -187,7 +188,7 @@ void LinkEnergyWifi::update()
    *  - if idle i.e. get_usage = 0, update P_{stat}
    * P_{tot} = P_{dyn}+P_{stat}
    */
-  if (link_->get_usage() != 0.0) {
+  if (link_->get_load() != 0.0) {
     eDyn_ += /*duration * */ durUsage * ((host_count * pRx_) + pTx_);
     eStat_ += (duration - durUsage) * pIdle_ * (host_count + 1);
     XBT_DEBUG("eDyn +=  %f * ((%f * %f) + %f) | eDyn = %f (durusage =%f)", durUsage, host_count, pRx_, pTx_, eDyn_,
@@ -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 1afdd6e..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"
@@ -111,7 +112,7 @@ void LinkLoad::update()
              " Please track your link with sg_link_load_track before trying to access any of its load metrics.",
              link_->get_cname());
 
-  double current_instantaneous_bytes_per_second = link_->get_usage();
+  double current_instantaneous_bytes_per_second = link_->get_load();
   double now                                    = simgrid::s4u::Engine::get_clock();
 
   // Update minimum/maximum observed values if needed
@@ -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/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..c464f04
--- /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 const& 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 const& 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 const& 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 const& 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..a6ada2f 100644 (file)
@@ -289,6 +289,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 +312,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 +337,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 +358,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 +408,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 9e3d59f..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
 {
@@ -112,6 +112,16 @@ Disk* Disk::set_write_bandwidth_profile(kernel::profile::Profile* profile)
                                        [this, profile]() { this->pimpl_->set_write_bandwidth_profile(profile); });
   return this;
 }
+int Disk::get_concurrency_limit() const
+{
+  return pimpl_->get_concurrency_limit();
+}
+
+Disk* Disk::set_concurrency_limit(int limit)
+{
+  kernel::actor::simcall_object_access(pimpl_, [this, limit] { pimpl_->set_concurrency_limit(limit); });
+  return this;
+}
 
 IoPtr Disk::io_init(sg_size_t size, Io::OpType type) 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 ba4a844..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);
     });
   }
 }
@@ -164,6 +167,16 @@ void Host::route_to(const Host* dest, std::vector<Link*>& links, double* latency
   for (auto* l : linkImpls)
     links.push_back(l->get_iface());
 }
+std::pair<std::vector<Link*>, double> Host::route_to(const Host* dest) const
+{
+  std::vector<kernel::resource::StandardLinkImpl*> linkImpls;
+  std::vector<Link*> links;
+  double latency = 0;
+  this->route_to(dest, linkImpls, &latency);
+  for (auto* l : linkImpls)
+    links.push_back(l->get_iface());
+  return std::make_pair(links, latency);
+}
 
 /** @brief Just like Host::routeTo, but filling an array of link implementations */
 void Host::route_to(const Host* dest, std::vector<kernel::resource::StandardLinkImpl*>& links, double* latency) const
@@ -207,6 +220,17 @@ Host* Host::set_properties(const std::unordered_map<std::string, std::string>& p
   return this;
 }
 
+int Host::get_concurrency_limit() const
+{
+  return pimpl_cpu_->get_concurrency_limit();
+}
+
+Host* Host::set_concurrency_limit(int limit)
+{
+  kernel::actor::simcall_object_access(pimpl_cpu_, [this, limit] { pimpl_cpu_->set_concurrency_limit(limit); });
+  return this;
+}
+
 /** Specify a profile turning the host on and off according to an exhaustive list or a stochastic law.
  * The profile must contain boolean values. */
 Host* Host::set_state_profile(kernel::profile::Profile* p)
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 8d41270..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;
@@ -130,9 +130,9 @@ Link* Link::set_concurrency_limit(int limit)
   return this;
 }
 
-double Link::get_usage() const
+double Link::get_load() const
 {
-  return this->pimpl_->get_constraint()->get_usage();
+  return this->pimpl_->get_constraint()->get_load();
 }
 
 void Link::turn_on()
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 3452c3b..1428132 100644 (file)
@@ -50,10 +50,10 @@ void sg_version()
   XBT_HELP("This program was linked against %s (git: %s), found in %s.", SIMGRID_VERSION_STRING, SIMGRID_GIT_VERSION,
            SIMGRID_INSTALL_PREFIX);
 
-#if SIMGRID_HAVE_MC
-  XBT_HELP("   Model-checking support compiled in.");
+#if SIMGRID_HAVE_STATEFUL_MC
+  XBT_HELP("   Stateful model-checking support compiled in.");
 #else
-  XBT_HELP("   Model-checking support disabled at compilation.");
+  XBT_HELP("   Stateful model-checking support disabled at compilation.");
 #endif
 
 #if SIMGRID_HAVE_NS3
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 614a00e..513694b 100644 (file)
 #include <boost/algorithm/string.hpp> /* trim */
 #include <boost/tokenizer.hpp>
 
-#if SIMGRID_HAVE_MC
 #include "src/mc/mc_config.hpp"
 #include "src/mc/mc_replay.hpp"
-#endif
 
 #if defined(__APPLE__)
 # include <AvailabilityMacros.h>
@@ -257,9 +255,9 @@ void smpi_init_options_internal(bool called_by_smpi_main)
   simgrid::config::declare_flag<std::string>(
       "smpi/privatization", "How we should privatize global variable at runtime (no, yes, mmap, dlopen).",
       default_privatization, [](const std::string& smpi_privatize_option) {
-        if (smpi_privatize_option == "no" || smpi_privatize_option == "0")
+        if (smpi_privatize_option == "no" || smpi_privatize_option == "0" || smpi_privatize_option == "OFF")
           _smpi_cfg_privatization = SmpiPrivStrategies::NONE;
-        else if (smpi_privatize_option == "yes" || smpi_privatize_option == "1")
+        else if (smpi_privatize_option == "yes" || smpi_privatize_option == "1" || smpi_privatize_option == "ON")
           _smpi_cfg_privatization = SmpiPrivStrategies::DEFAULT;
         else if (smpi_privatize_option == "mmap")
           _smpi_cfg_privatization = SmpiPrivStrategies::MMAP;
@@ -308,7 +306,6 @@ void smpi_init_options_internal(bool called_by_smpi_main)
 
 void smpi_check_options()
 {
-#if SIMGRID_HAVE_MC
   if (MC_is_active() || MC_record_replay_is_active()) {
     if (_sg_mc_buffering == "zero")
       simgrid::config::set_value<int>("smpi/send-is-detached-thresh", 0);
@@ -317,7 +314,6 @@ void smpi_check_options()
     else
       THROW_IMPOSSIBLE;
   }
-#endif
 
   xbt_assert(smpi_cfg_async_small_thresh() <= smpi_cfg_detached_send_thresh(),
              "smpi/async-small-thresh (=%d) should be smaller or equal to smpi/send-is-detached-thresh (=%d)",
@@ -336,4 +332,3 @@ void smpi_check_options()
   simgrid::smpi::colls::set_collectives();
   simgrid::smpi::colls::smpi_coll_cleanup_callback = nullptr;
 }
-
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 6e19b61..6e38668 100644 (file)
@@ -35,7 +35,6 @@ public:
   }
   void serialize(std::stringstream& stream) const override;
   std::string to_string() const override;
-  bool is_visible() const override { return true; }
 };
 void ObjectAccessObserver::serialize(std::stringstream& stream) const
 {
@@ -67,11 +66,10 @@ static ObjectOwner* get_owner(void* object)
 {
   if (owners.empty())
     std::atexit(clean_owners);
-  auto it = owners.find(object);
-  if (it != owners.end())
+  if (auto it = owners.find(object); it != owners.end())
     return it->second;
   auto* o = new ObjectOwner(nullptr);
-  owners.insert({object, o});
+  owners.emplace(object, o);
   return o;
 }
 
index 5cd89d5..fb22a59 100644 (file)
@@ -7,7 +7,7 @@
 #define _GNU_SOURCE
 
 #include "mmprivate.h"
-
+#include "src/mc/mc_environ.h" // MC_ENV_SOCKET_FD
 #include <dlfcn.h>
 #include <math.h>
 #include <stdlib.h>
index d3cdc24..7dff4e7 100644 (file)
 
 #include "src/internal_config.h"
 
-/** Environment variable name used to pass the communication socket.
- *
- * It is set by `simgrid-mc` to enable MC support in the children processes.
- *
- * It is placed in this file so that it's visible from mmalloc and MC without sharing anythin of xbt in mmalloc
- */
-#define MC_ENV_SOCKET_FD "SIMGRID_MC_SOCKET_FD"
-
 #include <stdio.h>     /* for NULL */
 #include <sys/types.h> /* for size_t */
 
index e865995..60c73ea 100644 (file)
@@ -12,8 +12,8 @@
 namespace simgrid::xbt {
 
 template <typename T> struct ref_or_value {
-  using type = std::conditional_t<std::is_lvalue_reference<T>::value,
-                                  std::reference_wrapper<typename std::remove_reference<T>::type>, T>;
+  using type =
+      std::conditional_t<std::is_lvalue_reference_v<T>, std::reference_wrapper<typename std::remove_reference_t<T>>, T>;
 };
 template <typename T> using ref_or_value_t = typename ref_or_value<T>::type;
 
@@ -48,7 +48,7 @@ private:
   friend constexpr iterator_wrapping<IteratorType, Arguments...> make_iterator_wrapping_explicit(Arguments... args);
 
 public:
-  iterator_wrapping(Args&&... begin_iteration) : m_args(std::forward<ref_or_value_t<Args>>(begin_iteration)...) {}
+  iterator_wrapping(Args&&... begin_iteration) : m_args(ref_or_value_t<Args>(begin_iteration)...) {}
   iterator_wrapping(const iterator_wrapping&)            = delete;
   iterator_wrapping(iterator_wrapping&&)                 = delete;
   iterator_wrapping& operator=(const iterator_wrapping&) = delete;
@@ -70,7 +70,7 @@ constexpr iterator_wrapping<Iterator, Args...> make_iterator_wrapping(Args&&...
 template <typename Iterator, typename... Args>
 constexpr iterator_wrapping<Iterator, Args...> make_iterator_wrapping_explicit(Args... args)
 {
-  return iterator_wrapping<Iterator, Args...>(std::forward<Args>(args)...);
+  return iterator_wrapping<Iterator, Args...>(args...);
 }
 
 } // namespace simgrid::xbt
index 1ce6a93..b83bad7 100644 (file)
@@ -29,7 +29,7 @@ namespace simgrid::xbt {
 template <class Iterator>
 struct powerset_iterator : public boost::iterator_facade<powerset_iterator<Iterator>, const std::vector<Iterator>,
                                                          boost::forward_traversal_tag> {
-  powerset_iterator() = default;
+  powerset_iterator()                                                                 = default;
   explicit powerset_iterator(Iterator begin, Iterator end = Iterator());
 
 private:
@@ -40,8 +40,6 @@ private:
   std::optional<subsets_iterator<Iterator>> current_subset_iter     = std::nullopt;
   std::optional<subsets_iterator<Iterator>> current_subset_iter_end = std::nullopt;
 
-  const std::vector<Iterator> empty_subset = std::vector<Iterator>();
-
   // boost::iterator_facade<...> interface to implement
   void increment();
   bool equal(const powerset_iterator<Iterator>& other) const;
@@ -70,13 +68,14 @@ template <typename Iterator> const std::vector<Iterator>& powerset_iterator<Iter
   if (current_subset_iter.has_value()) {
     return *current_subset_iter.value();
   }
+  static const std::vector<Iterator> empty_subset;
   return empty_subset;
 }
 
 template <typename Iterator> void powerset_iterator<Iterator>::increment()
 {
-  if (!current_subset_iter.has_value() || !current_subset_iter_end.has_value() ||
-      !current_subset_iter.has_value() || !iterator_end.has_value()) {
+  if (!current_subset_iter.has_value() || !current_subset_iter_end.has_value() || !current_subset_iter.has_value() ||
+      !iterator_end.has_value()) {
     return; // We've traversed all subsets at this point, or we're the "last" iterator
   }
 
index 41bdf41..59c95ae 100644 (file)
@@ -65,8 +65,8 @@ TEST_CASE("simgrid::xbt::powerset_iterator: Iteration General Properties")
       }
     }
 
-    for (const auto& iter : element_counts) {
-      REQUIRE(iter.second == expected_count);
+    for (const auto& [_, count] : element_counts) {
+      REQUIRE(count == expected_count);
     }
   }
 }
@@ -187,4 +187,4 @@ TEST_CASE("simgrid::xbt::variable_for_loop: Edge Cases")
               (outer_loop1.size() * outer_loop2.size() * outer_loop3.size() * outer_loop4.size() * outer_loop5.size()));
     }
   }
-}
\ No newline at end of file
+}
index bd5d29a..cbcb0a2 100644 (file)
@@ -85,11 +85,11 @@ template <typename IterableType> void variable_for_loop<IterableType>::increment
 
   for (auto j = k; j != std::numeric_limits<size_t>::max(); j--) {
     // Attempt to move to the next element of the `j`th collection
-    const auto& new_position = ++current_subset[j];
+    ++current_subset[j];
 
     // If the `j`th element has reached its own end, reset it
     // back to the beginning and keep moving forward
-    if (new_position == underlying_collections[j].get().cend()) {
+    if (current_subset[j] == underlying_collections[j].get().cend()) {
       current_subset[j] = underlying_collections[j].get().cbegin();
     } else {
       // Otherwise we've found the largest element which needed to
index 129923d..7addb33 100644 (file)
@@ -1,4 +1,4 @@
-if (NOT SIMGRID_HAVE_MC)
+if (NOT SIMGRID_HAVE_STATEFUL_MC)
   set(dwarf_disable 1)
   set(dwarf-expression_disable 1)
 endif()
@@ -40,17 +40,18 @@ set(teshsuite_src  ${teshsuite_src}
 set(tesh_files     ${tesh_files}    ${CMAKE_CURRENT_SOURCE_DIR}/random-bug/random-bug-replay.tesh
                                     ${CMAKE_CURRENT_SOURCE_DIR}/mutex-handling/without-mutex-handling.tesh PARENT_SCOPE)
 
+ADD_TESH(mc-random-bug-replay                --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/random-bug --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/random-bug random-bug-replay.tesh)
 IF(SIMGRID_HAVE_MC)
-  ADD_TESH(tesh-mc-dwarf                       --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/dwarf            --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/dwarf dwarf.tesh)
-  ADD_TESH(tesh-mc-dwarf-expression            --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/dwarf-expression --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/dwarf-expression dwarf-expression.tesh)
   ADD_TESH(tesh-mc-mutex-handling              --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/mutex-handling --setenv srcdir=${CMAKE_HOME_DIRECTORY} --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/mutex-handling mutex-handling.tesh --cfg=model-check/reduction:none)
   ADD_TESH(tesh-mc-mutex-handling-dpor         --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/mutex-handling --setenv srcdir=${CMAKE_HOME_DIRECTORY} --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/mutex-handling mutex-handling.tesh --cfg=model-check/reduction:dpor)
   ADD_TESH(tesh-mc-without-mutex-handling      --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/mutex-handling --setenv srcdir=${CMAKE_HOME_DIRECTORY} --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/mutex-handling without-mutex-handling.tesh --cfg=model-check/reduction:none)
   ADD_TESH(tesh-mc-without-mutex-handling-dpor --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/mutex-handling --setenv srcdir=${CMAKE_HOME_DIRECTORY} --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/mutex-handling without-mutex-handling.tesh --cfg=model-check/reduction:dpor)
   ADD_TESH(mc-random-bug                       --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/random-bug --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/random-bug random-bug.tesh)
 ENDIF()
-
-ADD_TESH(mc-random-bug-replay                  --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/random-bug --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/random-bug random-bug-replay.tesh)
+IF(SIMGRID_HAVE_STATEFUL_MC)
+  ADD_TESH(tesh-mc-dwarf                       --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/dwarf            --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/dwarf dwarf.tesh)
+  ADD_TESH(tesh-mc-dwarf-expression            --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/dwarf-expression --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/dwarf-expression dwarf-expression.tesh)
+ENDIF()
 
 if(enable_coverage)
   ADD_TEST(cover-mc-mutex-handling ${CMAKE_CURRENT_BINARY_DIR}/mutex-handling/mutex-handling ${CMAKE_HOME_DIRECTORY}/examples/platforms/small_platform.xml)
index 426c301..278ad03 100644 (file)
@@ -1,4 +1,4 @@
 #!/usr/bin/env tesh
 ! expect return 1
 ! output display
-$ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/mutex-handling ${srcdir:=.}/examples/platforms/small_platform.xml
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/mutex-handling ${srcdir:=.}/examples/platforms/small_platform.xml
index 53136bf..f594963 100644 (file)
@@ -2,4 +2,4 @@
 ! expect return 1
 ! output display
 ! timeout 30
-$ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/without-mutex-handling ${srcdir:=.}/examples/platforms/small_platform.xml
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/without-mutex-handling ${srcdir:=.}/examples/platforms/small_platform.xml
index 9bade15..80b910e 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 <csignal>
 #include <cstring>
 #include <simgrid/modelchecker.h>
 #include <simgrid/s4u.hpp>
@@ -30,11 +31,8 @@ static void app()
     if (x == 3 && y == 4)
       abort();
   } else if (behavior == Behavior::SEGV) {
-#ifndef __clang_analyzer__
-    int* A = nullptr;
     if (x == 3 && y == 4)
-      *A = 1;
-#endif
+      raise(SIGSEGV); // Simulate a segfault without displeasing the static analyzers
   } else {
     DIE_IMPOSSIBLE;
   }
index fc245fb..46ab37a 100644 (file)
@@ -1,6 +1,6 @@
 #!/usr/bin/env tesh
 ! expect return 1
-$ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/random-bug assert ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/random-bug assert ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning
 > [  0.000000] (0:maestro@) Behavior: assert
 > [  0.000000] (0:maestro@) Start a DFS exploration. Reduction is: dpor.
 > [  0.000000] (0:maestro@) **************************
@@ -10,11 +10,11 @@ $ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/random-bug assert ${platfdir
 > [  0.000000] (0:maestro@)   1: Random([0;5] ~> 3)
 > [  0.000000] (0:maestro@)   1: Random([0;5] ~> 4)
 > [  0.000000] (0:maestro@) You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1/3;1/4'
-> [  0.000000] (0:maestro@) DFS exploration ended. 20 unique states visited; 15 backtracks (47 transition replays, 12 states visited overall)
+> [  0.000000] (0:maestro@) DFS exploration ended. 27 unique states visited; 22 backtracks (19 transition replays, 68 states visited overall)
 
 ! expect return 6
 # because SIMGRID_MC_EXIT_PROGRAM_CRASH = 6
-$ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/random-bug abort ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning --log=no_loc
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/random-bug abort ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning --log=no_loc
 > [  0.000000] (0:maestro@) Behavior: abort
 > [  0.000000] (0:maestro@) Start a DFS exploration. Reduction is: dpor.
 > [  0.000000] (0:maestro@) **************************
@@ -25,18 +25,18 @@ $ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/random-bug abort ${platfdir}
 > [  0.000000] (0:maestro@)   1: Random([0;5] ~> 3)
 > [  0.000000] (0:maestro@)   1: Random([0;5] ~> 4)
 > [  0.000000] (0:maestro@) You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1/3;1/4'
-> [  0.000000] (0:maestro@) DFS exploration ended. 20 unique states visited; 15 backtracks (47 transition replays, 12 states visited overall)
+> [  0.000000] (0:maestro@) DFS exploration ended. 27 unique states visited; 22 backtracks (19 transition replays, 68 states visited overall)
 > [  0.000000] (0:maestro@) Stack trace not displayed because you passed --log=no_loc
 
-$ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/random-bug printf ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/random-bug printf ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning
 > [  0.000000] (0:maestro@) Behavior: printf
 > [  0.000000] (0:maestro@) Start a DFS exploration. Reduction is: dpor.
 > [  0.000000] (1:app@Fafard) Error reached
-> [  0.000000] (0:maestro@) DFS exploration ended. 43 unique states visited; 35 backtracks (108 transition replays, 30 states visited overall)
+> [  0.000000] (0:maestro@) DFS exploration ended. 43 unique states visited; 35 backtracks (30 transition replays, 108 states visited overall)
 
 ! expect return 6
 # because SIMGRID_MC_EXIT_PROGRAM_CRASH = 6
-$ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/random-bug segv ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning --log=no_loc
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/random-bug segv ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning --log=no_loc
 > [  0.000000] (0:maestro@) Behavior: segv
 > [  0.000000] (0:maestro@) Start a DFS exploration. Reduction is: dpor.
 > Segmentation fault.
@@ -48,5 +48,5 @@ $ ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/random-bug segv ${platfdir}/
 > [  0.000000] (0:maestro@)   1: Random([0;5] ~> 3)
 > [  0.000000] (0:maestro@)   1: Random([0;5] ~> 4)
 > [  0.000000] (0:maestro@) You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1/3;1/4'
-> [  0.000000] (0:maestro@) DFS exploration ended. 20 unique states visited; 15 backtracks (47 transition replays, 12 states visited overall)
+> [  0.000000] (0:maestro@) DFS exploration ended. 27 unique states visited; 22 backtracks (19 transition replays, 68 states visited overall)
 > [  0.000000] (0:maestro@) Stack trace not displayed because you passed --log=no_loc
index 83969f8..56d9e61 100644 (file)
@@ -105,7 +105,7 @@ if(SIMGRID_HAVE_NS3)
     add_executable       (${x}  EXCLUDE_FROM_ALL ${x}/${x}.cpp)
     target_link_libraries(${x}  simgrid)
     set_target_properties(${x}  PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${x})
-    add_dependencies(tests ${x})
+    add_dependencies(tests-ns3 ${x})
     ADD_TESH(tesh-s4u-${x} --setenv srcdir=${CMAKE_HOME_DIRECTORY}/teshsuite/s4u/${x} --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms --cd ${CMAKE_BINARY_DIR}/teshsuite/s4u/${x} ${CMAKE_HOME_DIRECTORY}/teshsuite/s4u/${x}/${x}.tesh)
   endforeach()
 endif()
index 2a3d976..5fdbb08 100644 (file)
@@ -1,47 +1,49 @@
 ! output sort
 
-$ ${bindir:=.}/basic-parsing-test ${srcdir:=.}/../../platforms/one_cluster.xml --log=root.fmt=%m%n
+$ ${bindir:=.}/basic-parsing-test --cfg=path:${srcdir:=.}/../../platforms one_cluster.xml --log=root.fmt=%m%n
 > Workstation number: 5, link number: 12
 
 ! output sort
-$ ${bindir:=.}/basic-parsing-test ${srcdir:=.}/../../platforms/host_attributes.xml --log=root.fmt=%m%n
+$ ${bindir:=.}/basic-parsing-test --cfg=path:${srcdir:=.}/../../platforms host_attributes.xml --log=root.fmt=%m%n
 > Workstation number: 5, link number: 1
 
 ! output sort
-$ ${bindir:=.}/basic-parsing-test ${srcdir:=.}/../../platforms/link_attributes.xml --log=root.fmt=%m%n
+$ ${bindir:=.}/basic-parsing-test --cfg=path:${srcdir:=.}/../../platforms link_attributes.xml --log=root.fmt=%m%n
 > Workstation number: 1, link number: 5
 
 ! output sort
-$ ${bindir:=.}/basic-parsing-test ${srcdir:=.}/../../platforms/three_hosts_non_symmetric_route.xml --log=root.fmt=%m%n
+$ ${bindir:=.}/basic-parsing-test --cfg=path:${srcdir:=.}/../../platforms three_hosts_non_symmetric_route.xml --log=root.fmt=%m%n
 > Workstation number: 3, link number: 4
 
 ! output sort
-$ ${bindir:=.}/basic-parsing-test ${srcdir:=.}/../../platforms/two_clusters.xml --log=root.fmt=%m%n
+$ ${bindir:=.}/basic-parsing-test --cfg=path:${srcdir:=.}/../../platforms two_clusters.xml --log=root.fmt=%m%n
 > Workstation number: 4, link number: 12
 
 ! output sort
-$ ${bindir:=.}/basic-parsing-test ${srcdir:=.}/../../platforms/two_hosts_multi_hop.xml --log=root.fmt=%m%n
+$ ${bindir:=.}/basic-parsing-test --cfg=path:${srcdir:=.}/../../platforms two_hosts_multi_hop.xml --log=root.fmt=%m%n
 > Workstation number: 2, link number: 4
 
 ! output sort
-$ ${bindir:=.}/basic-parsing-test ${srcdir:=.}/../../platforms/two_hosts_one_link.xml --log=root.fmt=%m%n
+$ ${bindir:=.}/basic-parsing-test --cfg=path:${srcdir:=.}/../../platforms two_hosts_one_link.xml --log=root.fmt=%m%n
 > Workstation number: 2, link number: 2
 
 ! output sort
-$ ${bindir:=.}/basic-parsing-test ${srcdir:=.}/../../platforms/four_hosts_floyd.xml --log=root.fmt=%m%n
+$ ${bindir:=.}/basic-parsing-test --cfg=path:${srcdir:=.}/../../platforms four_hosts_floyd.xml --log=root.fmt=%m%n
 > Workstation number: 4, link number: 5
 
 ! output sort
 $ ${bindir:=.}/basic-parsing-test ${platfdir:=.}/cloud.xml --log=root.fmt=%m%n
 > Workstation number: 510, link number: 1056
 
-$ ${bindir:=.}/basic-parsing-test ${srcdir:=.}/../../platforms/properties.xml --log=root.fmt=%m%n
+$ ${bindir:=.}/basic-parsing-test --cfg=path:${srcdir:=.}/../../platforms properties.xml --log=root.fmt=%m%n
 > Configuration change: Set 'cpu/optim' to 'TI'
+> The custom configuration 'path' is already defined by user!
 > Configuration change: Set 'precision/work-amount' to '0.000010'
 > Workstation number: 1, link number: 1
 
-$ ${bindir:=.}/basic-parsing-test ${srcdir:=.}/../../platforms/properties.xml --cfg=cpu/optim:TI --log=root.fmt=%m%n
+$ ${bindir:=.}/basic-parsing-test --cfg=path:${srcdir:=.}/../../platforms properties.xml --cfg=cpu/optim:TI --log=root.fmt=%m%n
 > Configuration change: Set 'cpu/optim' to 'TI'
 > The custom configuration 'cpu/optim' is already defined by user!
+> The custom configuration 'path' is already defined by user!
 > Configuration change: Set 'precision/work-amount' to '0.000010'
 > Workstation number: 1, link number: 1
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 ff4e558..b6f9251 100644 (file)
@@ -3,6 +3,6 @@ p We just want to check that the ns-3 bindings of SimGrid are working correctly,
 
 $ ./ns3-from-src-to-itself ${platfdir}/ns3-big-cluster.xml --cfg=network/model:ns-3 "--log=root.fmt:[%h:%a(%i)]%e[%c/%p]%e%m%n"
 > [:maestro(0)] [xbt_cfg/INFO] Configuration change: Set 'network/model' to 'ns-3'
-> [:maestro(0)] [res_ns3/WARNING] Sending from a host c-01.rennes to itself is not supported by ns-3. Every such communication finishes immediately upon startup.
+> [:maestro(0)] [res_ns3/WARNING] Sending from a host c-01.rennes to itself is not supported by ns-3. Every such communication finishes immediately upon startup in the SimGrid+ns-3 system.
 > [c-01.rennes:receiver(1)] [s4u_test/INFO] Done receiving from 2 senders, each of them sending 5 messages
 
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 be77453..ce735ed 100644 (file)
@@ -141,7 +141,7 @@ if(enable_smpi)
   if (NOT HAVE_SANITIZER_ADDRESS)
     ADD_TESH(tesh-smpi-coll-allreduce-with-leaks --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/smpi/coll-allreduce-with-leaks --cd ${CMAKE_BINARY_DIR}/teshsuite/smpi/coll-allreduce-with-leaks ${CMAKE_HOME_DIRECTORY}/teshsuite/smpi/coll-allreduce-with-leaks/coll-allreduce-with-leaks.tesh)
     set_target_properties(coll-allreduce-with-leaks PROPERTIES COMPILE_FLAGS "-trace-call-location")
-    if(enable_model-checking)
+    if(SIMGRID_HAVE_MC)
       add_dependencies(tests-mc coll-allreduce-with-leaks)
       ADD_TESH(tesh-mc-smpi-coll-allreduce-with-leaks --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/smpi/coll-allreduce-with-leaks --cd ${CMAKE_BINARY_DIR}/teshsuite/smpi/coll-allreduce-with-leaks ${CMAKE_HOME_DIRECTORY}/teshsuite/smpi/coll-allreduce-with-leaks/mc-coll-allreduce-with-leaks.tesh)
     endif()
index a07c97c..35613f1 100644 (file)
@@ -9,10 +9,10 @@ file(GLOB generator_scripts *Generator.py)
 
 if (enable_smpi_MBI_testsuite)
   if (NOT enable_smpi)
-    message(FATAL_ERROR "MBI test suite cannot be enabled without SMPI. Please change either setting.")
+    message(FATAL_ERROR "The MBI test suite cannot be enabled without SMPI. Please change either setting.")
   endif()
-  if (NOT enable_model-checking)
-    message(FATAL_ERROR "MBI test suite cannot be enabled without the Mc SimGrid model-checker. Please change either setting.")
+  if (NOT SIMGRID_HAVE_MC)
+    message(FATAL_ERROR "The MBI test suite cannot be enabled without the model-checker. Please change either setting.")
   endif()
 
   message(STATUS "Generating the MBI test cases")
index a427ef6..a0f2785 100644 (file)
@@ -347,6 +347,17 @@ $ $VALGRIND_NO_LEAK_CHECK ${bindir:=.}/../../../smpi_script/bin/smpirun -wrapper
 > 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
@@ -391,7 +402,194 @@ $ $VALGRIND_NO_LEAK_CHECK ${bindir:=.}/../../../smpi_script/bin/smpirun -wrapper
 > 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
@@ -403,4 +601,4 @@ $ $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. 623 unique states visited; 173 backtracks (3904 transition replays, 3108 states visited overall)
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 936 unique states visited; 254 backtracks (4843 transition replays, 6033 states visited overall)
\ No newline at end of file
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 53e2835..5ef4600 100644 (file)
@@ -17,7 +17,7 @@ if(enable_compile_warnings AND enable_compile_optimizations)
   SET(BUILDNAME "FULL_FLAGS" CACHE INTERNAL "Buildname" FORCE)
 endif()
 
-if(SIMGRID_HAVE_MC)
+if(SIMGRID_HAVE_STATEFUL_MC)
   SET(BUILDNAME "MODEL-CHECKING" CACHE INTERNAL "Buildname" FORCE)
 endif()
 
index ce4633f..28c0892 100644 (file)
@@ -454,6 +454,8 @@ set(PLUGINS_SRC
   src/plugins/vm/VmLiveMigration.hpp
   src/plugins/vm/dirty_page_tracking.cpp
   src/plugins/battery.cpp
+  src/plugins/task.cpp
+  src/plugins/photovoltaic.cpp
   )
 
 
@@ -519,13 +521,58 @@ set(MC_SRC_BASE
   src/mc/mc_replay.hpp
   src/mc/transition/Transition.cpp
   )
-
-set(MC_SRC
-  src/mc/explo/CommunicationDeterminismChecker.cpp
+  
+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
+  src/mc/api/RemoteApp.hpp
+  
   src/mc/explo/DFSExplorer.cpp
   src/mc/explo/DFSExplorer.hpp
   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
+  src/mc/remote/Channel.hpp
+  src/mc/remote/CheckerSide.cpp
+  src/mc/remote/CheckerSide.hpp
+  src/mc/remote/RemotePtr.hpp
+  src/mc/remote/mc_protocol.h
+
+  src/mc/transition/Transition.hpp
+  src/mc/transition/TransitionActorJoin.cpp
+  src/mc/transition/TransitionActorJoin.hpp
+  src/mc/transition/TransitionAny.cpp
+  src/mc/transition/TransitionAny.hpp
+  src/mc/transition/TransitionComm.cpp
+  src/mc/transition/TransitionComm.hpp
+  src/mc/transition/TransitionObjectAccess.cpp
+  src/mc/transition/TransitionObjectAccess.hpp
+  src/mc/transition/TransitionRandom.cpp
+  src/mc/transition/TransitionRandom.hpp
+  src/mc/transition/TransitionSynchro.cpp
+  src/mc/transition/TransitionSynchro.hpp
+  )
+
+set(MC_SRC_STATEFUL
+  src/mc/explo/CommunicationDeterminismChecker.cpp
   src/mc/explo/LivenessChecker.cpp
   src/mc/explo/LivenessChecker.hpp
   src/mc/explo/UdporChecker.cpp
@@ -536,6 +583,8 @@ set(MC_SRC
   src/mc/explo/udpor/Configuration.cpp
   src/mc/explo/udpor/EventSet.cpp
   src/mc/explo/udpor/EventSet.hpp
+  src/mc/explo/udpor/ExtensionSetCalculator.cpp
+  src/mc/explo/udpor/ExtensionSetCalculator.hpp
   src/mc/explo/udpor/History.cpp
   src/mc/explo/udpor/History.hpp
   src/mc/explo/udpor/maximal_subsets_iterator.cpp
@@ -565,15 +614,6 @@ set(MC_SRC
   src/mc/inspect/mc_unw.cpp
   src/mc/inspect/mc_unw.hpp
   src/mc/inspect/mc_unw_vmread.cpp
-
-  src/mc/remote/AppSide.cpp
-  src/mc/remote/AppSide.hpp
-  src/mc/remote/Channel.cpp
-  src/mc/remote/Channel.hpp
-  src/mc/remote/CheckerSide.cpp
-  src/mc/remote/CheckerSide.hpp
-  src/mc/remote/RemotePtr.hpp
-  src/mc/remote/mc_protocol.h
   
   src/mc/sosp/ChunkedData.cpp
   src/mc/sosp/ChunkedData.hpp
@@ -586,38 +626,21 @@ set(MC_SRC
   src/mc/sosp/Snapshot.cpp
   src/mc/sosp/Snapshot.hpp
 
-  src/mc/transition/Transition.hpp
-  src/mc/transition/TransitionActorJoin.cpp
-  src/mc/transition/TransitionActorJoin.hpp
-  src/mc/transition/TransitionAny.cpp
-  src/mc/transition/TransitionAny.hpp
-  src/mc/transition/TransitionComm.cpp
-  src/mc/transition/TransitionComm.hpp
-  src/mc/transition/TransitionObjectAccess.cpp
-  src/mc/transition/TransitionObjectAccess.hpp
-  src/mc/transition/TransitionRandom.cpp
-  src/mc/transition/TransitionRandom.hpp
-  src/mc/transition/TransitionSynchro.cpp
-  src/mc/transition/TransitionSynchro.hpp
-
-  src/mc/api/ActorState.hpp
-  src/mc/api/State.cpp
-  src/mc/api/State.hpp
-  src/mc/api/RemoteApp.cpp
-  src/mc/api/RemoteApp.hpp
-
   src/mc/AddressSpace.hpp
   src/mc/VisitedState.cpp
   src/mc/VisitedState.hpp
   src/mc/compare.cpp
+  src/mc/mc_environ.h
   src/mc/mc_exit.hpp
   src/mc/mc_forward.hpp
   src/mc/mc_private.hpp
   src/mc/mc_record.cpp
 
   src/mc/api/strategy/BasicStrategy.hpp
+  src/mc/api/strategy/MaxMatchComm.hpp
+  src/mc/api/strategy/MinMatchComm.hpp
   src/mc/api/strategy/Strategy.hpp
-  src/mc/api/strategy/WaitStrategy.hpp
+  src/mc/api/strategy/UniformStrategy.hpp
   
   src/xbt/mmalloc/mm_interface.c
   )
@@ -632,12 +655,14 @@ set(headers_to_install
   include/simgrid/exec.h
   include/simgrid/Exception.hpp
   include/simgrid/chrono.hpp
+  include/simgrid/plugins/battery.hpp
   include/simgrid/plugins/dvfs.h
   include/simgrid/plugins/energy.h
-  include/simgrid/plugins/battery.hpp
   include/simgrid/plugins/file_system.h
   include/simgrid/plugins/live_migration.h
   include/simgrid/plugins/load.h
+  include/simgrid/plugins/task.hpp
+  include/simgrid/plugins/photovoltaic.hpp
   include/simgrid/plugins/ProducerConsumer.hpp
   include/simgrid/instr.h
   include/simgrid/mailbox.h
@@ -769,8 +794,12 @@ if(enable_smpi)
 endif()
 
 if(SIMGRID_HAVE_MC)
-  set(simgrid_sources  ${simgrid_sources}  ${MC_SRC})
+  set(simgrid_sources  ${simgrid_sources}  ${MC_SRC_STATELESS})
+endif()
+if(SIMGRID_HAVE_STATEFUL_MC)
+  set(simgrid_sources  ${simgrid_sources}  ${MC_SRC_STATEFUL})
 endif()
+set(EXTRA_DIST ${EXTRA_DIST} ${MC_SRC_STATELESS} ${MC_SRC_STATEFUL})
 
 if(SIMGRID_HAVE_NS3)
   set(headers_to_install ${headers_to_install} include/simgrid/plugins/ns3.hpp)
@@ -1058,6 +1087,7 @@ set(CMAKE_SOURCE_FILES
   tools/cmake/scripts/update_tesh.pl
   tools/cmake/test_prog/prog_asan.cpp
   tools/cmake/test_prog/prog_makecontext.c
+  tools/cmake/test_prog/prog_ns3.cpp
   tools/cmake/test_prog/prog_stackgrowth.c
   tools/cmake/test_prog/prog_stacksetup.c
   tools/cmake/test_prog/prog_tsan.cpp
@@ -1110,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 11cf6b9..f41f3b7 100644 (file)
@@ -42,13 +42,13 @@ if(HAVE_MMALLOC)
                 APPEND PROPERTY INCLUDE_DIRECTORIES "${INTERNAL_INCLUDES}")
 endif()
 
-if(enable_model-checking)
+if(SIMGRID_HAVE_MC)
   add_executable(simgrid-mc ${MC_SIMGRID_MC_SRC})
   target_link_libraries(simgrid-mc simgrid)
   set_target_properties(simgrid-mc
                         PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
   set_property(TARGET simgrid-mc
-               APPEND PROPERTY INCLUDE_DIRECTORIES "${INTERNAL_INCLUDES}")
+                APPEND PROPERTY INCLUDE_DIRECTORIES "${INTERNAL_INCLUDES}")
   install(TARGETS simgrid-mc # install that binary without breaking the rpath on Mac
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}/)
   add_dependencies(tests-mc simgrid-mc)
index 57f6771..860a65e 100644 (file)
@@ -25,9 +25,9 @@ if(HAVE_CDT_LIB AND HAVE_CGRAPH_LIB AND HAVE_CGRAPH_H)
   include_directories(${file_graphviz_h} ${file_graphviz_h}/graphviz)
   link_directories(${lib_graphviz})
 
-  set(HAVE_GRAPHVIZ "1")
+  set(HAVE_GRAPHVIZ ON)
 else()
-  set(HAVE_GRAPHVIZ "0")
+  set(HAVE_GRAPHVIZ OFF)
 endif()
 
 mark_as_advanced(HAVE_GRAPHVIZ)
index 7154c2b..9975872 100644 (file)
@@ -126,8 +126,19 @@ else()
   endforeach()
 endif()
 
+set(SIMGRID_HAVE_NS3_GetNextEventTime FALSE)
 if(SIMGRID_HAVE_NS3)
-  message(STATUS "ns-3 found (v${NS3_VERSION}; minor:${NS3_MINOR_VERSION}; patch:${NS3_PATCH_VERSION}; libpath: ${NS3_LIBRARY_PATH}).")
+  try_compile(compile_ns3 ${CMAKE_BINARY_DIR} ${CMAKE_HOME_DIRECTORY}/tools/cmake/test_prog/prog_ns3.cpp
+              CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${NS3_INCLUDE_DIR}" "-DLINK_DIRECTORIES=${NS3_LIBRARY_PATH}"
+              LINK_LIBRARIES "${NS3_LIBRARIES}"
+              OUTPUT_VARIABLE compile_ns3_output)
+  if(NOT compile_ns3)
+    message(STATUS "ns-3 does not have the ns3::Simulator::GetNextEventTime patch. The ns-3 model will not be idempotent. Compilation output:\n ${compile_ns3_output}")
+  else()
+    set(SIMGRID_HAVE_NS3_GetNextEventTime TRUE)
+  endif()
+  file(REMOVE ${CMAKE_BINARY_DIR}/prog_ns3)
+  message(STATUS "ns-3 found (v${NS3_VERSION}; minor ver:${NS3_MINOR_VERSION}; patch ver:${NS3_PATCH_VERSION}; GetNextEventTime patch: ${SIMGRID_HAVE_NS3_GetNextEventTime}; libpath: ${NS3_LIBRARY_PATH}).")
   link_directories(${NS3_LIBRARY_PATH})
   include_directories(${NS3_INCLUDE_DIR})
 else()
index 8427cfe..28b2b35 100644 (file)
@@ -17,7 +17,7 @@
 #  PAPI_LIBRARY          The PAPI library
 #  PAPI_INCLUDE_DIRS       The location of PAPI headers
 
-set(HAVE_PAPI 0)
+set(HAVE_PAPI OFF)
 set(PAPI_HINT ${papi_path} CACHE PATH "Path to search for PAPI headers and library")
 
 find_path(PAPI_PREFIX
@@ -55,7 +55,7 @@ endif()
 
 if (PAPI_LIBRARY)
   if(PAPI_INCLUDE_DIRS)
-    set(HAVE_PAPI 1)
+    set(HAVE_PAPI ON)
     mark_as_advanced(HAVE_PAPI)
   endif()
 endif()
index 0ff2fcc..c4faa2f 100644 (file)
@@ -38,7 +38,7 @@ endif()
 option(minimal-bindings      "Whether to compile the Python bindings libraries with the minimal dependency set" off)
 mark_as_advanced(minimal-bindings)
 
-option(enable_model-checking "Turn this on to experiment with our prototype of model-checker (hinders the simulation's performance even if turned off at runtime)" off)
+option(enable_model-checking "Turn this on to experiment with our prototype of model-checker" off)
 option(enable-model-checking "Please set 'enable_model-checking' instead" off)
 mark_as_advanced(enable-model-checking)
 if(enable-model-checking)
index ce7d830..09491e4 100644 (file)
@@ -19,6 +19,7 @@ IF(enable_memcheck)
       SET(VALGRIND_WRAPPER ${VALGRIND_WRAPPER}\ --xml=yes\ --xml-file=memcheck_test_%p.memcheck\ --child-silent-after-fork=yes\ )
     endif()
     set(TESH_OPTION ${TESH_OPTION} --setenv VALGRIND_NO_LEAK_CHECK=--leak-check=no\ --show-leak-kinds=none)
+    set(TESH_OPTION ${TESH_OPTION} --setenv VALGRIND_NO_TRACE_CHILDREN=--trace-children=no)
 
 #    message(STATUS "tesh wrapper: ${VALGRIND_WRAPPER}")
 
@@ -131,12 +132,15 @@ 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
                   src/mc/explo/udpor/History_test.cpp
                   src/mc/explo/udpor/Configuration_test.cpp)
-if (SIMGRID_HAVE_MC)
+if (SIMGRID_HAVE_STATEFUL_MC)
   set(UNIT_TESTS ${UNIT_TESTS} ${MC_UNIT_TESTS})
 else()
   set(EXTRA_DIST ${EXTRA_DIST} ${MC_UNIT_TESTS})
index b13a4fc..d2ec57b 100755 (executable)
@@ -8,6 +8,7 @@
 use strict;
 use warnings;
 
+# Many other parameters (such as trace-children) are set in Tests.cmake
 my @argv = ("valgrind", "--quiet");
 my $count = 0;
 
diff --git a/tools/cmake/test_prog/prog_ns3.cpp b/tools/cmake/test_prog/prog_ns3.cpp
new file mode 100644 (file)
index 0000000..c763cbd
--- /dev/null
@@ -0,0 +1,7 @@
+#include "ns3/simulator.h"
+
+int main()
+{
+  ns3::Simulator::GetNextEventTime();
+  return 0;
+}
\ No newline at end of file
index 8fdaa14..8ce4d26 100644 (file)
@@ -1,5 +1,5 @@
-# Base image
-FROM debian:11
+# Base image: bookworm is Debian 12
+FROM debian:bookworm
 
 # Install the dependencies:
 #  - of the website
index a1a6b16..9c8fc76 100755 (executable)
@@ -209,7 +209,6 @@ cmake -G"$GENERATOR" ${INSTALL:+-DCMAKE_INSTALL_PREFIX=$INSTALL} \
   -DLTO_EXTRA_FLAG="auto" \
   -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
   "$SRCFOLDER"
-set +x
 
 make -j $NUMBER_OF_PROCESSORS VERBOSE=1 tests