container: simgrid/unstable
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Build and test BatSim
run: |
set -e
options: --user 0
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Build and test BigDFT
run: |
set -e
container: simgrid/unstable
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Build and test StarPU
run: |
set -e
container: simgrid/unstable
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Build and test WRENCH
run: |
set -e
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
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
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
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
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
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Init options
run: |
echo "CC=${{ matrix.config.cc }}" >> $GITHUB_ENV
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: build
run: |
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:
possible.
- Allow to set a concurrency limit on disks and hosts, as it was already the case for links.
- Rename Link::get_usage() to Link::get_load() for consistency with Host::
+ - Every signal now come with a static version that is invoked for every object of that class,
+ and an instance version that is invoked for this specific object only. For example,
+ s4u::Actor::on_suspend_cb() adds a callback that is invoked for the suspend of any actor while
+ s4u::Actor::on_this_suspend_cb() adds a callback for this specific actor only.
+ - Activity::on_suspended_cb() is renamed to Activity::on_suspend_cb(), and fired right before the suspend.
+ - Activity::on_resumed_cb() is renamed to Activity::on_resume_cb(), and fired right before the resume.
+ - Resource::on_state_change_cb() is renamed to Resource::on_onoff_cb() to distinguish from the
+ Activity::on_state_change_cb() that is related to the activity state machine, not on/off.
+ - Activity signals (veto, suspend, resume, completion) are now specialized by activity class.
+ That is, callbacks registered in Exec::on_suspend_cb will not be fired for Comms nor Ios.
+
+New S4U plugins:
+ - Task: They are designed to represent dataflows, i.e, graphs of repeatable Activities.
+ See the examples under examples/cpp/task-* and the documentation in the Plugins page.
+ - Battery: Enable the management of batteries on hosts.
+ See the examples under examples/cpp/battery-* and the documentation in the Plugins page.
+ - Photovoltaic: Enable the management of photovoltaic panels on hosts.
+ See the examples under examples/cpp/photovoltaic-* and the documentation in the Plugins page.
-New plugin: Operation
- - Operations are designed to represent workflows, i.e, graphs of repeatable Activities.
- - Documentation: https://simgrid.frama.io/simgrid/Plugins.html#operation
- - Examples: examples/cpp/operation-*
-
-New plugin: Battery
- - Enable the management of batteries on hosts.
- - Documentation: https://simgrid.frama.io/simgrid/Plugins.html#battery
- - Examples: examples/cpp/battery-*
-
Kernel:
- optimize an internal datastructure (use a set instead of a list for ongoing activities),
leading to a potentially big performance gain, in particular with many detached comms.
- Synchronize the MBI tests with upstream.
- Show the full actor bactraces when replaying a MC trace (with model-check/replay)
and the status of all actors on deadlocks in MC mode.
- - The safety/stateless aspects of the MC are now enabled by default in all simgrid builds.
- Liveness and stateful aspects are still controled by the enabling_model-checking
+ - The safety/stateless aspects of the MC are now enabled by default in all simgrid builds.
+ Liveness and stateful aspects are still controled by the enabling_model-checking
configuration option.
- Stateless model-checking is now usable on any system, including Mac OSX and ARM processors.
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
# (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.
#
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
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 ()
include examples/cpp/network-ns3/s4u-network-ns3.cpp
include examples/cpp/network-wifi/s4u-network-wifi.cpp
include examples/cpp/network-wifi/s4u-network-wifi.tesh
-include examples/cpp/operation-simple/s4u-operation-simple.cpp
-include examples/cpp/operation-simple/s4u-operation-simple.tesh
-include examples/cpp/operation-switch-host/s4u-operation-switch-host.cpp
-include examples/cpp/operation-switch-host/s4u-operation-switch-host.tesh
-include examples/cpp/operation-variable-load/s4u-operation-variable-load.cpp
-include examples/cpp/operation-variable-load/s4u-operation-variable-load.tesh
+include examples/cpp/task-io/s4u-task-io.cpp
+include examples/cpp/task-io/s4u-task-io.tesh
+include examples/cpp/task-simple/s4u-task-simple.cpp
+include examples/cpp/task-simple/s4u-task-simple.tesh
+include examples/cpp/task-switch-host/s4u-task-switch-host.cpp
+include examples/cpp/task-switch-host/s4u-task-switch-host.tesh
+include examples/cpp/task-variable-load/s4u-task-variable-load.cpp
+include examples/cpp/task-variable-load/s4u-task-variable-load.tesh
+include examples/cpp/photovoltaic-simple/s4u-photovoltaic-simple.cpp
+include examples/cpp/photovoltaic-simple/s4u-photovoltaic-simple.tesh
include examples/cpp/platform-comm-serialize/s4u-platform-comm-serialize.cpp
include examples/cpp/platform-comm-serialize/s4u-platform-comm-serialize.tesh
include examples/cpp/platform-failures/s4u-platform-failures.cpp
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
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
include include/simgrid/plugins/live_migration.h
include include/simgrid/plugins/load.h
include include/simgrid/plugins/ns3.hpp
-include include/simgrid/plugins/operation.hpp
+include include/simgrid/plugins/task.hpp
+include include/simgrid/plugins/photovoltaic.hpp
include include/simgrid/s4u.hpp
include include/simgrid/s4u/Activity.hpp
include include/simgrid/s4u/Actor.hpp
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
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
include src/plugins/link_energy.cpp
include src/plugins/link_energy_wifi.cpp
include src/plugins/link_load.cpp
-include src/plugins/operation.cpp
+include src/plugins/task.cpp
+include src/plugins/photovoltaic.cpp
include src/plugins/vm/VmLiveMigration.cpp
include src/plugins/vm/VmLiveMigration.hpp
include src/plugins/vm/dirty_page_tracking.cpp
@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
// 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");
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)
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
****************
.. doxygengroup:: plugin_battery
-.. _plugin_operation:
+.. _plugin_task:
+
+Task
+===========
+
+.. doxygengroup:: plugin_task
+
+.. _plugin_photovoltaic:
-Operation
+Photovoltaic
===========
-.. doxygengroup:: plugin_operation
+.. doxygengroup:: plugin_photovoltaic
.. LocalWords: SimGrid
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)
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
.. 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:
.. 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:
.. 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:
.. 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:
.. 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:
.. 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:
=============
.. 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:
=============
.. 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:
===========
.. 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:
=======================
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)
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)
mc-bugged1 mc-bugged1-liveness mc-bugged2 mc-bugged2-liveness mc-centralized-mutex mc-electric-fence mc-failing-assert
network-ns3 network-ns3-wifi network-wifi
io-async io-priority io-degradation io-file-system io-file-remote io-disk-raw io-dependent
- operation-simple operation-variable-load operation-switch-host
+ task-io task-simple task-variable-load task-switch-host
+ photovoltaic-simple
platform-comm-serialize platform-failures platform-profile platform-properties
plugin-host-load plugin-link-load plugin-prodcons
replay-comm replay-io
--setenv srcdir=${CMAKE_CURRENT_SOURCE_DIR}/${example}
--cd ${CMAKE_CURRENT_SOURCE_DIR}/${example}
${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}-nodpor.tesh)
- endif()
+ endif()
set(tesh_files ${tesh_files} ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}-statequality.tesh)
set(tesh_files ${tesh_files} ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}-nodpor.tesh)
endforeach()
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
#!/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
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());
}
});
}
}
- 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();
#!/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
}
}
- 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();
#!/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
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();
#!/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
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
$ ${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
// We use the user data field to store the finish time of the predecessor of the comm, i.e., its potential
// start time
data_available = *comm->get_data<double>() + redist_time;
- }
+ }
/* no transfer, control dependency */
- if (const auto* exec = dynamic_cast<sg4::Exec*>(parent.get()))
- data_available = exec->get_finish_time();
+ if (const auto* parent_exec = dynamic_cast<sg4::Exec*>(parent.get()))
+ data_available = parent_exec->get_finish_time();
if (last_data_available < data_available)
last_data_available = data_available;
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);
auto dax = sg4::create_DAG_from_DAX(argv[2]);
/* Schedule the root first */
- double finish_time;
+ double root_finish_time;
auto* root = static_cast<sg4::Exec*>(dax.front().get());
- auto host = get_best_host(root, &finish_time);
+ auto* host = get_best_host(root, &root_finish_time);
schedule_on(root, host);
e.run();
}
/* Cleanup memory */
- for (auto const& h : e.get_all_hosts())
+ for (auto const* h : e.get_all_hosts())
delete h->get_data<double>();
XBT_INFO("Simulation Time: %f", simgrid_get_clock());
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.
$ ${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
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();
#!/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
#ifndef _KADEMLIA_TASK_HPP_
#define _KADEMLIA_TASK_HPP_
+#include "answer.hpp"
#include "s4u-dht-kademlia.hpp"
#include "simgrid/s4u.hpp"
// ========= 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");
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(),
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);
> [0.000000] [mc_explo/INFO] 3: iSend(mbox=0)
> [0.000000] [mc_explo/INFO] 1: WaitComm(from 3 to 1, mbox=0, no timeout)
> [0.000000] [mc_explo/INFO] You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1;3;1;1;3;3;1'
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 551 unique states visited; 137 backtracks (2239 transition replays, 1551 states visited overall)
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1070 unique states visited; 252 backtracks (4082 transition replays, 2760 states visited overall)
! expect return 1
! timeout 20
> [0.000000] [mc_explo/INFO] *** PROPERTY NOT VALID ***
> [0.000000] [mc_explo/INFO] **************************
> [0.000000] [mc_explo/INFO] Counter-example execution trace:
-> [0.000000] [mc_explo/INFO] 3: iSend(mbox=0)
> [0.000000] [mc_explo/INFO] 1: iRecv(mbox=0)
+> [0.000000] [mc_explo/INFO] 3: iSend(mbox=0)
> [0.000000] [mc_explo/INFO] 1: WaitComm(from 3 to 1, mbox=0, no timeout)
> [0.000000] [mc_explo/INFO] 1: iRecv(mbox=0)
> [0.000000] [mc_explo/INFO] 3: WaitComm(from 3 to 1, mbox=0, no timeout)
> [0.000000] [mc_explo/INFO] 3: iSend(mbox=0)
> [0.000000] [mc_explo/INFO] 1: WaitComm(from 3 to 1, mbox=0, no timeout)
-> [0.000000] [mc_explo/INFO] You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'3;1;1;1;3;3;1'
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1161 unique states visited; 282 backtracks (4556 transition replays, 3113 states visited overall)
+> [0.000000] [mc_explo/INFO] You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1;3;1;1;3;3;1'
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 995 unique states visited; 253 backtracks (4006 transition replays, 2758 states visited overall)
+
> [: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'
+++ /dev/null
-/* Copyright (c) 2017-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* This example demonstrate basic use of the operation plugin.
- *
- * We model the following graph:
- *
- * exec1 -> comm -> exec2
- *
- * exec1 and exec2 are execution operations.
- * comm is a communication operation.
- */
-
-#include "simgrid/plugins/operation.hpp"
-#include "simgrid/s4u.hpp"
-
-XBT_LOG_NEW_DEFAULT_CATEGORY(operation_simple, "Messages specific for this operation example");
-
-int main(int argc, char* argv[])
-{
- simgrid::s4u::Engine e(&argc, argv);
- e.load_platform(argv[1]);
- simgrid::plugins::Operation::init();
-
- // Retrieve hosts
- auto tremblay = e.host_by_name("Tremblay");
- auto jupiter = e.host_by_name("Jupiter");
-
- // Create operations
- auto exec1 = simgrid::plugins::ExecOp::init("exec1", 1e9, tremblay);
- auto exec2 = simgrid::plugins::ExecOp::init("exec2", 1e9, jupiter);
- auto comm = simgrid::plugins::CommOp::init("comm", 1e7, tremblay, jupiter);
-
- // Create the graph by defining dependencies between operations
- exec1->add_successor(comm);
- comm->add_successor(exec2);
-
- // Add a function to be called when operations end for log purpose
- simgrid::plugins::Operation::on_end_cb([](const simgrid::plugins::Operation* op) {
- XBT_INFO("Operation %s finished (%d)", op->get_name().c_str(), op->get_count());
- });
-
- // Enqueue two executions for operation exec1
- exec1->enqueue_execs(2);
-
- // Start the simulation
- e.run();
- return 0;
-}
+++ /dev/null
-#!/usr/bin/env tesh
-
-$ ${bindir:=.}/s4u-operation-simple ${platfdir}/small_platform.xml
-> [10.194200] [operation_simple/INFO] Operation exec1 finished (1)
-> [11.714617] [operation_simple/INFO] Operation comm finished (1)
-> [20.388399] [operation_simple/INFO] Operation exec1 finished (2)
-> [21.908817] [operation_simple/INFO] Operation comm finished (2)
-> [24.821464] [operation_simple/INFO] Operation exec2 finished (1)
-> [37.928311] [operation_simple/INFO] Operation exec2 finished (2)
+++ /dev/null
-#!/usr/bin/env tesh
-
-$ ${bindir:=.}/s4u-operation-switch-host ${platfdir}/small_platform.xml
-> [1.520418] [operation_switch_host/INFO] Operation comm0 finished (1)
-> [2.873012] [operation_switch_host/INFO] Operation comm0 finished (2)
-> [4.393430] [operation_switch_host/INFO] Operation comm0 finished (3)
-> [5.746025] [operation_switch_host/INFO] Operation comm0 finished (4)
-> [14.627265] [operation_switch_host/INFO] Operation exec1 finished (1)
-> [15.979859] [operation_switch_host/INFO] Operation exec2 finished (1)
-> [16.147682] [operation_switch_host/INFO] Operation comm1 finished (1)
-> [17.332454] [operation_switch_host/INFO] Operation comm2 finished (1)
-> [27.734112] [operation_switch_host/INFO] Operation exec1 finished (2)
-> [29.086707] [operation_switch_host/INFO] Operation exec2 finished (2)
-> [29.254529] [operation_switch_host/INFO] Operation comm1 finished (2)
-> [30.439301] [operation_switch_host/INFO] Operation comm2 finished (2)
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env tesh
-
-$ ${bindir:=.}/s4u-operation-variable-load ${platfdir}/small_platform.xml
-> [Tremblay:input:(1) 0.000000] [operation_variable_load/INFO] --- Small load ---
-> [1.520418] [operation_variable_load/INFO] Operation comm finished (1)
-> [14.627265] [operation_variable_load/INFO] Operation exec finished (1)
-> [101.520418] [operation_variable_load/INFO] Operation comm finished (2)
-> [114.627265] [operation_variable_load/INFO] Operation exec finished (2)
-> [201.520418] [operation_variable_load/INFO] Operation comm finished (3)
-> [214.627265] [operation_variable_load/INFO] Operation exec finished (3)
-> [Tremblay:input:(1) 1000.000000] [operation_variable_load/INFO] --- Heavy load ---
-> [1001.520418] [operation_variable_load/INFO] Operation comm finished (4)
-> [1003.040835] [operation_variable_load/INFO] Operation comm finished (5)
-> [1004.561253] [operation_variable_load/INFO] Operation comm finished (6)
-> [1014.627265] [operation_variable_load/INFO] Operation exec finished (4)
-> [1027.734112] [operation_variable_load/INFO] Operation exec finished (5)
-> [1040.840959] [operation_variable_load/INFO] Operation exec finished (6)
--- /dev/null
+/* 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;
+}
--- /dev/null
+#!/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
> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/sleep-set' to 'true'
> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'actors' to '2'
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 130 unique states visited; 27 backtracks (209 transition replays, 52 states visited overall)
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 66 unique states visited; 11 backtracks (97 transition replays, 20 states visited overall)
p The stats without checkpoints is: 130 unique states visited; 27 backtracks (308 transition replays, 151 states visited overall)
p But it runs much faster (0.6 sec vs. 1.6 sec), damn slow checkpointing code.
> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/sleep-set' to 'true'
> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'actors' to '2'
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 130 unique states visited; 27 backtracks (308 transition replays, 151 states visited overall)
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 66 unique states visited; 11 backtracks (126 transition replays, 49 states visited overall)
$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/strategy:nb_wait -- ${bindir:=.}/s4u-synchro-mutex --cfg=actors:3 --log=s4u_test.thres:critical
> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/sleep-set' to 'true'
> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/strategy' to 'nb_wait'
> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'actors' to '3'
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 3492 unique states visited; 743 backtracks (12498 transition replays, 8263 states visited overall)
\ No newline at end of file
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 296 unique states visited; 52 backtracks (765 transition replays, 417 states visited overall)
\ No newline at end of file
--- /dev/null
+/* 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;
+}
--- /dev/null
+#!/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)
--- /dev/null
+/* 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;
+}
--- /dev/null
+#!/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)
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
-/* This example demonstrates how to dynamically modify a graph of operations.
+/* This example demonstrates how to dynamically modify a graph of tasks.
*
* Assuming we have two instances of a service placed on different hosts,
* we want to send data alternatively to thoses instances.
* With exec1 and exec2 on different hosts.
*/
-#include "simgrid/plugins/operation.hpp"
+#include "simgrid/plugins/task.hpp"
#include "simgrid/s4u.hpp"
-XBT_LOG_NEW_DEFAULT_CATEGORY(operation_switch_host, "Messages specific for this operation example");
+XBT_LOG_NEW_DEFAULT_CATEGORY(task_switch_host, "Messages specific for this task example");
int main(int argc, char* argv[])
{
simgrid::s4u::Engine e(&argc, argv);
e.load_platform(argv[1]);
- simgrid::plugins::Operation::init();
+ simgrid::plugins::Task::init();
// Retrieve hosts
auto* tremblay = e.host_by_name("Tremblay");
auto* jupiter = e.host_by_name("Jupiter");
auto* fafard = e.host_by_name("Fafard");
- // Create operations
- auto comm0 = simgrid::plugins::CommOp::init("comm0");
+ // Create tasks
+ auto comm0 = simgrid::plugins::CommTask::init("comm0");
comm0->set_bytes(1e7);
comm0->set_source(tremblay);
- auto exec1 = simgrid::plugins::ExecOp::init("exec1", 1e9, jupiter);
- auto exec2 = simgrid::plugins::ExecOp::init("exec2", 1e9, fafard);
- auto comm1 = simgrid::plugins::CommOp::init("comm1", 1e7, jupiter, tremblay);
- auto comm2 = simgrid::plugins::CommOp::init("comm2", 1e7, fafard, tremblay);
+ auto exec1 = simgrid::plugins::ExecTask::init("exec1", 1e9, jupiter);
+ auto exec2 = simgrid::plugins::ExecTask::init("exec2", 1e9, fafard);
+ auto comm1 = simgrid::plugins::CommTask::init("comm1", 1e7, jupiter, tremblay);
+ auto comm2 = simgrid::plugins::CommTask::init("comm2", 1e7, fafard, tremblay);
- // Create the initial graph by defining dependencies between operations
+ // Create the initial graph by defining dependencies between tasks
comm0->add_successor(exec2);
exec1->add_successor(comm1);
exec2->add_successor(comm2);
- // Add a function to be called when operations end for log purpose
- simgrid::plugins::Operation::on_end_cb([](const simgrid::plugins::Operation* op) {
- XBT_INFO("Operation %s finished (%d)", op->get_name().c_str(), op->get_count());
+ // Add a function to be called when tasks end for log purpose
+ simgrid::plugins::Task::on_end_cb([](const simgrid::plugins::Task* t) {
+ XBT_INFO("Task %s finished (%d)", t->get_name().c_str(), t->get_count());
});
// Add a function to be called before each executions of comm0
- // This function modifies the graph of operations by adding or removing
+ // This function modifies the graph of tasks by adding or removing
// successors to comm0
- comm0->on_this_start([exec1, exec2, jupiter, fafard](simgrid::plugins::Operation* op) {
- auto* comm0 = dynamic_cast<simgrid::plugins::CommOp*>(op);
+ comm0->on_this_start([exec1, exec2, jupiter, fafard](simgrid::plugins::Task* t) {
+ auto* comm0 = dynamic_cast<simgrid::plugins::CommTask*>(t);
static int count = 0;
if (count % 2 == 0) {
comm0->set_destination(jupiter);
count++;
});
- // Enqueue four executions for operation comm0
+ // Enqueue four executions for task comm0
comm0->enqueue_execs(4);
// Start the simulation
--- /dev/null
+#!/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
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
-/* This example demonstrates how to create a variable load for operations.
+/* This example demonstrates how to create a variable load for tasks.
*
* We consider the following graph:
*
* comm -> exec
*
- * With a small load each comm operation is followed by an exec operation.
- * With a heavy load there is a burst of comm before the exec operation can even finish once.
+ * With a small load each comm task is followed by an exec task.
+ * With a heavy load there is a burst of comm before the exec task can even finish once.
*/
-#include "simgrid/plugins/operation.hpp"
+#include "simgrid/plugins/task.hpp"
#include "simgrid/s4u.hpp"
-XBT_LOG_NEW_DEFAULT_CATEGORY(operation_variable_load, "Messages specific for this s4u example");
+XBT_LOG_NEW_DEFAULT_CATEGORY(task_variable_load, "Messages specific for this s4u example");
-static void variable_load(simgrid::plugins::OperationPtr op)
+static void variable_load(simgrid::plugins::TaskPtr t)
{
XBT_INFO("--- Small load ---");
for (int i = 0; i < 3; i++) {
- op->enqueue_execs(1);
+ t->enqueue_execs(1);
simgrid::s4u::this_actor::sleep_for(100);
}
simgrid::s4u::this_actor::sleep_until(1000);
XBT_INFO("--- Heavy load ---");
for (int i = 0; i < 3; i++) {
- op->enqueue_execs(1);
+ t->enqueue_execs(1);
simgrid::s4u::this_actor::sleep_for(1);
}
}
{
simgrid::s4u::Engine e(&argc, argv);
e.load_platform(argv[1]);
- simgrid::plugins::Operation::init();
+ simgrid::plugins::Task::init();
// Retreive hosts
auto tremblay = e.host_by_name("Tremblay");
auto jupiter = e.host_by_name("Jupiter");
- // Create operations
- auto comm = simgrid::plugins::CommOp::init("comm", 1e7, tremblay, jupiter);
- auto exec = simgrid::plugins::ExecOp::init("exec", 1e9, jupiter);
+ // Create tasks
+ auto comm = simgrid::plugins::CommTask::init("comm", 1e7, tremblay, jupiter);
+ auto exec = simgrid::plugins::ExecTask::init("exec", 1e9, jupiter);
- // Create the graph by defining dependencies between operations
+ // Create the graph by defining dependencies between tasks
comm->add_successor(exec);
- // Add a function to be called when operations end for log purpose
- simgrid::plugins::Operation::on_end_cb([](const simgrid::plugins::Operation* op) {
- XBT_INFO("Operation %s finished (%d)", op->get_name().c_str(), op->get_count());
+ // Add a function to be called when tasks end for log purpose
+ simgrid::plugins::Task::on_end_cb([](const simgrid::plugins::Task* t) {
+ XBT_INFO("Task %s finished (%d)", t->get_name().c_str(), t->get_count());
});
// Create the actor that will inject load during the simulation
--- /dev/null
+#!/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)
> %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
> 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
--- /dev/null
+<?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>
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)
--- /dev/null
+# Copyright (c) 2006-2023. The SimGrid Team. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the license (GNU LGPL) which comes with this package.
+
+from argparse import ArgumentParser
+import sys
+from simgrid import Engine, Task, ExecTask, IoTask, IoOpType
+
+def parse():
+ parser = ArgumentParser()
+ parser.add_argument(
+ '--platform',
+ type=str,
+ required=True,
+ help='path to the platform description'
+ )
+ return parser.parse_args()
+
+def callback( t):
+ print(f'[{Engine.clock}] { t} finished ({ t.count})')
+
+if __name__ == '__main__':
+ args = parse()
+ e = Engine(sys.argv)
+ e.load_platform(args.platform)
+ Task.init()
+
+ # Retrieve hosts
+ bob = e.host_by_name('bob')
+ carl = e.host_by_name('carl')
+
+ # Create tasks
+ exec1 = ExecTask.init("exec1", 1e9, bob)
+ exec2 = ExecTask.init("exec2", 1e9, carl)
+ write = IoTask.init("write", 1e7, bob.disks[0], IoOpType.WRITE)
+ read = IoTask.init("read", 1e7, carl.disks[0], IoOpType.READ)
+
+ # Create the graph by defining dependencies between tasks
+ exec1.add_successor(write)
+ write.add_successor(read)
+ read.add_successor(exec2)
+
+ # Add a function to be called when tasks end for log purpose
+ Task.on_end_cb(callback)
+
+ # Enqueue two executions for task exec1
+ exec1.enqueue_execs(2)
+
+ # runs the simulation
+ e.run()
\ No newline at end of file
--- /dev/null
+#!/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)
+
--- /dev/null
+# 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()
+
--- /dev/null
+#!/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)
--- /dev/null
+# 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()
--- /dev/null
+#!/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)
--- /dev/null
+# Copyright (c) 2006-2023. The SimGrid Team. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the license (GNU LGPL) which comes with this package.
+
+"""
+This example demonstrates how to create a variable load for tasks.
+We consider the following graph:
+
+comm -> exec
+
+With a small load each comm task is followed by an exec task.
+With a heavy load there is a burst of comm before the exec task can even finish once.
+"""
+
+from argparse import ArgumentParser
+import sys
+from simgrid import Engine, Task, CommTask, ExecTask, Actor, this_actor
+
+def parse():
+ parser = ArgumentParser()
+ parser.add_argument(
+ '--platform',
+ type=str,
+ required=True,
+ help='path to the platform description'
+ )
+ return parser.parse_args()
+
+def callback( t):
+ print(f'[{Engine.clock}] { t} finished ({ t.count})')
+
+def variable_load( t):
+ print('--- Small load ---')
+ for i in range(3):
+ t.enqueue_execs(1)
+ this_actor.sleep_for(100)
+ this_actor.sleep_for(1000)
+ print('--- Heavy load ---')
+ for i in range(3):
+ t.enqueue_execs(1)
+ this_actor.sleep_for(1)
+
+if __name__ == '__main__':
+ args = parse()
+ e = Engine(sys.argv)
+ e.load_platform(args.platform)
+ Task.init()
+
+ # Retrieve hosts
+ tremblay = e.host_by_name('Tremblay')
+ jupiter = e.host_by_name('Jupiter')
+
+ # Create tasks
+ comm = CommTask.init("comm", 1e7, tremblay, jupiter)
+ exec = ExecTask.init("exec", 1e9, jupiter)
+
+ # Create the graph by defining dependencies between tasks
+ comm.add_successor(exec)
+
+ # Add a function to be called when tasks end for log purpose
+ Task.on_end_cb(callback)
+
+ # Create the actor that will inject load during the simulation
+ Actor.create("input", tremblay, variable_load, comm)
+
+ # runs the simulation
+ e.run()
--- /dev/null
+#!/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)
+
+
> 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
> 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] **************************
> [0.000000] [mc_global/INFO] 3: MUTEX_WAIT(mutex: 1, owner: 3)
> [0.000000] [mc_global/INFO] 3: MUTEX_ASYNC_LOCK(mutex: 0, owner: 2)
> [0.000000] [mc_Session/INFO] You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'2;2;3;2;3;3'
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 19 unique states visited; 1 backtracks (22 transition replays, 2 states visited overall)
\ No newline at end of file
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 28 unique states visited; 2 backtracks (37 transition replays, 7 states visited overall)
\ No newline at end of file
> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/sleep-set' to 'true'
> [0.000000] [sthread/INFO] Starting the simulation.
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1101 unique states visited; 136 backtracks (2950 transition replays, 1713 states visited overall)
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 106 unique states visited; 17 backtracks (295 transition replays, 172 states visited overall)
$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/sleep-set:true --cfg=model-check/strategy:nb_wait --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-producer-consumer -q -c 2 -C 1 -p 2 -P 1
> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/sleep-set' to 'true'
> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/strategy' to 'nb_wait'
> [0.000000] [sthread/INFO] Starting the simulation.
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1101 unique states visited; 136 backtracks (2950 transition replays, 1713 states visited overall)
\ No newline at end of file
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 107 unique states visited; 18 backtracks (300 transition replays, 175 states visited overall)
\ No newline at end of file
}
namespace resource {
class Action;
+class CpuAction;
class CpuImpl;
class Model;
class Resource;
#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 = "");
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
#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
*
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 */
#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
{
static bool execute_all();
};
-} // namespace timer
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::timer
#endif /* SRC_KERNEL_TIMER_TIMER_HPP_ */
#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 =
/** @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_; };
void set_suspend_state(Action::SuspendStates state) { suspended_ = state; }
};
-} // namespace resource
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::resource
#endif
#include <simgrid/kernel/resource/Action.hpp>
#include <unordered_map>
-namespace simgrid {
-namespace kernel {
-namespace resource {
+namespace simgrid::kernel::resource {
class XBT_PUBLIC Model {
public:
ActionHeap action_heap_;
};
-} // namespace resource
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::resource
#endif
#include <unordered_map>
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
/**
* @brief Placeholder for old ClusterZone class
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_ */
#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
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_ */
#include <simgrid/kernel/routing/ClusterZone.hpp>
#include <simgrid/s4u/Link.hpp>
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
class DragonflyRouter {
public:
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
#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
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_ */
#include <simgrid/kernel/routing/ClusterZone.hpp>
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
class XBT_PRIVATE FatTreeLink;
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
#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
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_ */
#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
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_ */
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.
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);
#include <unordered_set>
#include <vector>
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
class Route {
public:
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 */
#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)
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_ */
#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
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_ */
#include <vector>
-namespace simgrid {
-namespace kernel {
-namespace routing {
+namespace simgrid::kernel::routing {
/** @ingroup ROUTING_API
* @brief NetZone using a Torus topology
static std::vector<unsigned long> parse_topo_parameters(const std::string& topo_parameters);
};
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
#endif
#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
std::vector<double> coords;
};
} // namespace vivaldi
-} // namespace routing
-} // namespace kernel
-} // namespace simgrid
+} // namespace simgrid::kernel::routing
#endif /* SIMGRID_ROUTING_VIVALDI_HPP_ */
#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
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_ */
/** 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>>;
*/
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;
}
*/
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");
*/
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())
}
};
-} // namespace plugin
-} // namespace simgrid
+} // namespace simgrid::plugin
#endif // SIMGRID_PLUGIN_PRODUCERCONSUMER_HPP
+++ /dev/null
-#ifndef SIMGRID_PLUGINS_OPERATION_H_
-#define SIMGRID_PLUGINS_OPERATION_H_
-
-#include <simgrid/s4u/Activity.hpp>
-#include <xbt/Extendable.hpp>
-
-#include <atomic>
-#include <map>
-#include <memory>
-#include <set>
-
-namespace simgrid::plugins {
-
-class Operation;
-using OperationPtr = boost::intrusive_ptr<Operation>;
-XBT_PUBLIC void intrusive_ptr_release(Operation* o);
-XBT_PUBLIC void intrusive_ptr_add_ref(Operation* o);
-class ExecOp;
-using ExecOpPtr = boost::intrusive_ptr<ExecOp>;
-XBT_PUBLIC void intrusive_ptr_release(ExecOp* e);
-XBT_PUBLIC void intrusive_ptr_add_ref(ExecOp* e);
-class CommOp;
-using CommOpPtr = boost::intrusive_ptr<CommOp>;
-XBT_PUBLIC void intrusive_ptr_release(CommOp* c);
-XBT_PUBLIC void intrusive_ptr_add_ref(CommOp* c);
-
-struct ExtendedAttributeActivity {
- static simgrid::xbt::Extension<simgrid::s4u::Activity, ExtendedAttributeActivity> EXTENSION_ID;
- Operation* operation_;
-};
-
-class Operation {
-private:
- static bool inited_;
- std::set<Operation*> successors_ = {};
- std::map<Operation*, unsigned int> predecessors_ = {};
-
- void add_predecessor(Operation* predecessor);
- void remove_predecessor(Operation* predecessor);
- bool ready_to_run() const;
- void receive(Operation* source);
- void complete();
-
-protected:
- std::string name_;
- double amount_;
- int queued_execs_ = 0;
- int count_ = 0;
- bool working_ = false;
- s4u::ActivityPtr current_activity_;
- std::function<void(Operation*)> end_func_;
- std::function<void(Operation*)> start_func_;
- explicit Operation(const std::string& name);
- virtual ~Operation() = default;
- virtual void execute() = 0;
-
- static xbt::signal<void(Operation*)> on_start;
- static xbt::signal<void(Operation*)> on_end;
- std::atomic_int_fast32_t refcount_{0};
-
-public:
- static void init();
- const std::string& get_name() const { return name_; }
- const char* get_cname() const { return name_.c_str(); }
- void enqueue_execs(int n);
- void set_amount(double amount);
- double get_amount() const { return amount_; }
- void add_successor(OperationPtr op);
- void remove_successor(OperationPtr op);
- void remove_all_successors();
- const std::set<Operation*>& get_successors() const { return successors_ ;}
- void on_this_start(const std::function<void(Operation*)>& func);
- void on_this_end(const std::function<void(Operation*)>& func);
- int get_count() const;
-
- /** Add a callback fired before an operation activity start.
- * Triggered after the on_this_start function**/
- static void on_start_cb(const std::function<void(Operation*)>& cb) { on_start.connect(cb); }
- /** Add a callback fired after an operation activity end.
- * Triggered after the on_this_end function, but before
- * sending tokens to successors.**/
- static void on_end_cb(const std::function<void(Operation*)>& cb) { on_end.connect(cb); }
-
-#ifndef DOXYGEN
- friend void intrusive_ptr_release(Operation* o)
- {
- if (o->refcount_.fetch_sub(1, std::memory_order_release) == 1) {
- std::atomic_thread_fence(std::memory_order_acquire);
- delete o;
- }
- }
- friend void intrusive_ptr_add_ref(Operation* o) { o->refcount_.fetch_add(1, std::memory_order_relaxed); }
-#endif
-};
-
-class ExecOp : public Operation {
-private:
- s4u::Host* host_;
-
- explicit ExecOp(const std::string& name);
- void execute() override;
-
-public:
- static ExecOpPtr init(const std::string& name);
- static ExecOpPtr init(const std::string& name, double flops, s4u::Host* host);
- ExecOpPtr set_host(s4u::Host* host);
- s4u::Host* get_host() const { return host_; }
- ExecOpPtr set_flops(double flops);
- double get_flops() const { return get_amount(); }
- friend void inline intrusive_ptr_release(ExecOp* e) { intrusive_ptr_release(static_cast<Operation*>(e)); }
- friend void inline intrusive_ptr_add_ref(ExecOp* e) { intrusive_ptr_add_ref(static_cast<Operation*>(e)); }
-};
-
-class CommOp : public Operation {
-private:
- s4u::Host* source_;
- s4u::Host* destination_;
-
- explicit CommOp(const std::string& name);
- void execute() override;
-
-public:
- static CommOpPtr init(const std::string& name);
- static CommOpPtr init(const std::string& name, double bytes, s4u::Host* source,
- s4u::Host* destination);
- CommOpPtr set_source(s4u::Host* source);
- s4u::Host* get_source() const { return source_; }
- CommOpPtr set_destination(s4u::Host* destination);
- s4u::Host* get_destination() const { return destination_; }
- CommOpPtr set_bytes(double bytes);
- double get_bytes() const { return get_amount(); }
- friend void inline intrusive_ptr_release(CommOp* c) { intrusive_ptr_release(static_cast<Operation*>(c)); }
- friend void inline intrusive_ptr_add_ref(CommOp* c) { intrusive_ptr_add_ref(static_cast<Operation*>(c)); }
-};
-} // namespace simgrid::plugins
-#endif
--- /dev/null
+#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
--- /dev/null
+#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
#include <simgrid/forward.h>
#include <stdexcept>
#include <string>
+#include <string_view>
#include <vector>
#include <xbt/Extendable.hpp>
#include <xbt/asserts.h>
{
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);
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_;
* 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();
} else {
if (vetoed_activities_ != nullptr)
vetoed_activities_->insert(this);
- on_veto(*this);
+ fire_on_veto();
+ fire_on_this_veto();
}
}
// 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();
}
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);
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);
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);
}
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
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`.
* @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 */
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 */
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);
/** 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 */
*/
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.
/** 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 */
#include <atomic>
#include <future>
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
class XBT_PUBLIC Barrier {
kernel::activity::BarrierImpl* pimpl_;
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();
friend XBT_PUBLIC void intrusive_ptr_release(Barrier* barrier);
#endif
};
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
#endif
#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;
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);
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 :)
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. */
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 */
#include <future>
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
/**
* @beginrst
#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`).
void notify_all();
};
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
#endif
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
#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.
/** @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);
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);
}
}
#endif
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
#endif /* SIMGRID_S4U_ENGINE_HPP */
#include <simgrid/s4u/Actor.hpp>
#include <xbt/ex.h>
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
/** Computation Activity, representing the asynchronous executions.
*
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
bool is_assigned() const override;
};
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
#endif /* SIMGRID_S4U_EXEC_HPP */
#define SIMGRID_S4U_HOST_HPP
#include <simgrid/forward.h>
+#include <simgrid/kernel/resource/Action.hpp>
#include <xbt/Extendable.hpp>
#include <xbt/signal.hpp>
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) {}
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
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 */
Host* set_concurrency_limit(int limit);
int get_concurrency_limit() const;
- /** @brief Convert the CPU's speed from string to double */
+ /** \static @brief Convert the CPU's speed from string to double */
static std::vector<double> convert_pstate_speed_vector(const std::vector<std::string>& speed_per_state);
/**
* @brief Set the CPU's speed
#include <string>
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
/** I/O Activity, representing the asynchronous disk access.
*
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;
bool is_assigned() const override;
};
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
#endif /* SIMGRID_S4U_IO_HPP */
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);
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);
+ }
};
/**
/** @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);
};
#include <memory>
#include <string>
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
/** @brief Mailboxes: Network rendez-vous points. */
class XBT_PUBLIC Mailbox {
/** @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 */
get_async<T>(&res)->wait_for(timeout);
return res;
}
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
#endif /* SIMGRID_S4U_MAILBOX_HPP */
#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.
*
#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 */
#include <utility>
#include <vector>
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
/** @brief Networking Zones
*
#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); }
/**
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 */
#include <simgrid/forward.h>
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
/** @brief A classical semaphore, but blocking in the simulation world
*
#endif
public:
- /** Constructs a new semaphore */
+ /** \static Constructs a new semaphore */
static SemaphorePtr create(unsigned int initial_capacity);
void acquire();
bool would_block() const;
};
-} // namespace s4u
-} // namespace simgrid
+} // namespace simgrid::s4u
#endif /* SIMGRID_S4U_SEMAPHORE_HPP */
#include <simgrid/s4u/Host.hpp>
#include <xbt/utility.hpp>
-namespace simgrid {
-namespace s4u {
+namespace simgrid::s4u {
/** @brief Host extension for the VMs */
class VmHostExt {
/* 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
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
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.
*
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
#ifdef __cplusplus
#include <boost/intrusive_ptr.hpp>
-namespace simgrid {
-namespace smpi {
+namespace simgrid::smpi {
class Colls;
class Comm;
class Topo_Dist_Graph;
class Win;
-}
-}
+} // namespace simgrid::smpi
using SMPI_Comm = simgrid::smpi::Comm;
using SMPI_Datatype = simgrid::smpi::Datatype;
#include <limits>
#include <vector>
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
template<class T, class U> class Extension;
template<class T> class Extendable;
// 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
#include <string>
#include <unordered_map>
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
/** @brief a PropertyHolder can be given a set of textual properties
*
template <class Assoc> void set_properties(const Assoc& properties);
};
-} // namespace xbt
-} // namespace simgrid
+} // namespace simgrid::xbt
#endif
#include <xbt/automaton.h>
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
/** Add a proposition to an automaton (the C++ way)
*
a, id, [](auto* cb) -> int { return (*(F*)cb)(); }, callback, [](auto* cb) -> void { delete (F*)cb; });
}
-}
-}
+} // namespace simgrid::xbt
#endif
XBT_PUBLIC void xbt_backtrace_display_current();
SG_END_DECL
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
class BacktraceImpl;
/** A backtrace
void display() const;
};
-}
-}
+} // namespace simgrid::xbt
#endif
#include <xbt/sysdep.h>
#include <xbt/utility.hpp>
-namespace simgrid {
-namespace config {
+namespace simgrid::config {
class Config;
*/
// 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) {
}
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));
}
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)
{
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)
{
*/
// 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) {
XBT_PUBLIC void show_aliases();
XBT_PUBLIC void help();
-} // namespace config
-} // namespace simgrid
+} // namespace simgrid::config
#endif
#include <vector>
#include <xbt/base.h>
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
void path_push(std::string const& str);
void path_pop();
private:
std::string path_;
};
-}}
+} // namespace simgrid::xbt
#endif /* XBT_FILE_HPP */
#include <utility>
#include <vector>
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
template <class F> class MainFunction {
F code_;
* @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;
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);
return Task<decltype(code(std::move(args)...))()>(std::move(task));
}
-} // namespace xbt
-} // namespace simgrid
+} // namespace simgrid::xbt
#endif
#include <simgrid/Exception.hpp>
#include <xbt/log.h>
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
/** Display information about an exception
*
XBT_PUBLIC void install_exception_handler();
-} // namespace xbt
-} // namespace simgrid
+} // namespace simgrid::xbt
-#endif
\ No newline at end of file
+#endif
#include <utility>
#include <xbt/ex.h>
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
/** A value or an exception (or nothing)
*
{
fulfill_promise(promise, [&future] { return std::forward<F>(future).get(); });
}
-}
-}
+} // namespace simgrid::xbt
#endif
#include <random>
#include <string>
-namespace simgrid {
-namespace xbt {
-namespace random {
+namespace simgrid::xbt::random {
/** A random number generator.
*
* @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
#include <algorithm>
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
/** Describes a contiguous inclusive-exclusive [a,b) range of values */
template<class T> class Range {
bool contain(T const& x) const { return begin_ <= x && end_ > x; }
};
-}
-}
+} // namespace simgrid::xbt
#endif
#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>;
* 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);
#include <map>
#include <utility>
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
template <class S> class signal;
/** 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
#include <cstdlib>
#include <string>
-namespace simgrid {
-namespace xbt {
+namespace simgrid::xbt {
/** Create a C++ string from a C-style format
*
*/
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
#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`
*
return std::system_error(errno_code(), what);
}
-}
-}
+} // namespace simgrid::xbt
#endif
} \
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
*/
list.erase(list.iterator_to(elem));
}
-} // namespace xbt
-} // namespace simgrid
+} // namespace simgrid::xbt
#endif
# 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
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
#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>
#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;
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();
"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")
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
}
});
},
- "");
+ "")
+ .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)
"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)
.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)
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).")
.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>(),
.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>(),
.def("resume", &Actor::resume, py::call_guard<py::gil_scoped_release>(),
"Resume that actor, that was previously suspend()ed.")
.def_static("kill_all", &Actor::kill_all, py::call_guard<py::gil_scoped_release>(),
- "Kill all actors but the caller.");
+ "Kill all actors but the caller.")
+ .def(
+ "__repr__", [](const ActorPtr a) { return "Actor(" + a->get_name() + ")"; },
+ "Textual representation of the Actor");
+
+ /* Enum Class IoOpType */
+ py::enum_<simgrid::s4u::Io::OpType>(m, "IoOpType")
+ .value("READ", simgrid::s4u::Io::OpType::READ)
+ .value("WRITE", simgrid::s4u::Io::OpType::WRITE);
+
+ /* Class Task */
+ py::class_<Task, TaskPtr>(m, "Task", "Task. See the C++ documentation for details.")
+ .def_static("init", &Task::init)
+ .def_static(
+ "on_start_cb",
+ [](py::object cb) {
+ cb.inc_ref(); // keep alive after return
+ const py::gil_scoped_release gil_release;
+ Task::on_start_cb([cb](Task* op) {
+ const py::gil_scoped_acquire py_context; // need a new context for callback
+ py::reinterpret_borrow<py::function>(cb.ptr())(op);
+ });
+ },
+ "Add a callback called when each task starts.")
+ .def_static(
+ "on_end_cb",
+ [](py::object cb) {
+ cb.inc_ref(); // keep alive after return
+ const py::gil_scoped_release gil_release;
+ Task::on_end_cb([cb](Task* op) {
+ const py::gil_scoped_acquire py_context; // need a new context for callback
+ py::reinterpret_borrow<py::function>(cb.ptr())(op);
+ });
+ },
+ "Add a callback called when each task ends.")
+ .def_property_readonly("name", &Task::get_name, "The name of this task (read-only).")
+ .def_property_readonly("count", &Task::get_count, "The execution count of this task (read-only).")
+ .def_property_readonly("successors", &Task::get_successors, "The successors of this task (read-only).")
+ .def_property("amount", &Task::get_amount, &Task::set_amount, "The amount of work to do for this task.")
+ .def("enqueue_execs", py::overload_cast<int>(&Task::enqueue_execs), py::call_guard<py::gil_scoped_release>(),
+ py::arg("n"), "Enqueue executions for this task.")
+ .def("add_successor", py::overload_cast<TaskPtr>(&Task::add_successor), py::call_guard<py::gil_scoped_release>(),
+ py::arg("op"), "Add a successor to this task.")
+ .def("remove_successor", py::overload_cast<TaskPtr>(&Task::remove_successor),
+ py::call_guard<py::gil_scoped_release>(), py::arg("op"), "Remove a successor of this task.")
+ .def("remove_all_successors", &Task::remove_all_successors, py::call_guard<py::gil_scoped_release>(),
+ "Remove all successors of this task.")
+ .def("on_this_start", py::overload_cast<const std::function<void(Task*)>&>(&Task::on_this_start), py::arg("func"),
+ "Add a callback called when this task starts.")
+ .def("on_this_end", py::overload_cast<const std::function<void(Task*)>&>(&Task::on_this_end), py::arg("func"),
+ "Add a callback called when this task ends.")
+ .def(
+ "__repr__", [](const TaskPtr op) { return "Task(" + op->get_name() + ")"; },
+ "Textual representation of the Task");
+
+ /* Class CommTask */
+ py::class_<CommTask, CommTaskPtr, Task>(m, "CommTask", "Communication Task. See the C++ documentation for details.")
+ .def_static("init", py::overload_cast<const std::string&>(&CommTask::init),
+ py::call_guard<py::gil_scoped_release>(), py::arg("name"), "CommTask constructor")
+ .def_static("init", py::overload_cast<const std::string&, double, Host*, Host*>(&CommTask::init),
+ py::call_guard<py::gil_scoped_release>(), py::arg("name"), py::arg("bytes"), py::arg("source"),
+ py::arg("destination"), "CommTask constructor")
+ .def_property("source", &CommTask::get_source, &CommTask::set_source, "The source of the communication.")
+ .def_property("destination", &CommTask::get_destination, &CommTask::set_destination,
+ "The destination of the communication.")
+ .def_property("bytes", &CommTask::get_bytes, &CommTask::set_bytes, "The amount of bytes to send.")
+ .def(
+ "__repr__", [](const CommTaskPtr c) { return "CommTask(" + c->get_name() + ")"; },
+ "Textual representation of the CommTask");
+
+ /* Class ExecTask */
+ py::class_<ExecTask, ExecTaskPtr, Task>(m, "ExecTask", "Execution Task. See the C++ documentation for details.")
+ .def_static("init", py::overload_cast<const std::string&>(&ExecTask::init),
+ py::call_guard<py::gil_scoped_release>(), py::arg("name"), "ExecTask constructor")
+ .def_static("init", py::overload_cast<const std::string&, double, Host*>(&ExecTask::init),
+ py::call_guard<py::gil_scoped_release>(), py::arg("name"), py::arg("flops"), py::arg("host"),
+ "CommTask constructor.")
+ .def_property("host", &ExecTask::get_host, &ExecTask::set_host, "The host of the execution.")
+ .def_property("flops", &ExecTask::get_flops, &ExecTask::set_flops, "The amount of flops to execute.")
+ .def(
+ "__repr__", [](const ExecTaskPtr e) { return "ExecTask(" + e->get_name() + ")"; },
+ "Textual representation of the ExecTask");
+
+ /* Class IoTask */
+ py::class_<IoTask, IoTaskPtr, Task>(m, "IoTask", "IO Task. See the C++ documentation for details.")
+ .def_static("init", py::overload_cast<const std::string&>(&IoTask::init),
+ py::call_guard<py::gil_scoped_release>(), py::arg("name"), "IoTask constructor")
+ .def_static("init", py::overload_cast<const std::string&, double, Disk*, Io::OpType>(&IoTask::init),
+ py::call_guard<py::gil_scoped_release>(), py::arg("name"), py::arg("bytes"), py::arg("disk"),
+ py::arg("type"), "IoTask constructor.")
+ .def_property("disk", &IoTask::get_disk, &IoTask::set_disk, "The disk of the IO.")
+ .def_property("bytes", &IoTask::get_bytes, &IoTask::set_bytes, "The amount of bytes to process.")
+ .def_property("type", &IoTask::get_bytes, &IoTask::set_bytes, "The type of IO.")
+ .def(
+ "__repr__", [](const IoTaskPtr io) { return "IoTask(" + io->get_name() + ")"; },
+ "Textual representation of the IoTask");
}
#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
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) {
dag.push_back(current);
for (auto const& parent : task["parents"])
- successors[parent].push_back(current);
+ successors[parent.get<std::string>()].push_back(current);
}
// Assign successors
for (auto const& [parent, successors_list] : successors)
#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"
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,
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();
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);
});
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);
->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();
});
}
void ActivityImpl::suspend()
{
- if (model_action_ == nullptr)
+ if (model_action_ == nullptr) {
+ XBT_CRITICAL("POUET");
return;
+ }
XBT_VERB("This activity is suspended (remain: %f)", model_action_->get_remains());
+ get_iface()->fire_on_suspend();
+ get_iface()->fire_on_this_suspend();
model_action_->suspend();
- s4u::Activity::on_suspended(*get_iface());
}
void ActivityImpl::resume()
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()
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) {
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());
XBT_DEBUG("CommImpl::finish() comm %p, state %s, src_proc %p, dst_proc %p, detached: %d", this, get_state_str(),
src_actor_.get(), dst_actor_.get(), detached_);
- on_completion(*this);
+ if (get_iface()) {
+ const auto& piface = static_cast<const s4u::Comm&>(*get_iface());
+ s4u::Comm::on_completion(piface);
+ piface.on_this_completion(piface);
+ }
/* Update synchro state */
if (src_timeout_ && src_timeout_->get_state() == resource::Action::State::FINISHED)
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
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
undaemonize();
s4u::Actor::on_termination(*get_ciface());
+ get_ciface()->on_this_termination(*get_ciface());
while (not mailboxes_.empty())
mailboxes_.back()->set_receiver(nullptr);
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 << ' ';
}
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();
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 << ' ';
}
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();
{
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();
}
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)
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 */
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
void DiskImpl::destroy()
{
s4u::Disk::on_destruction(piface_);
+ piface_.on_this_destruction(piface_);
delete this;
}
{
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();
void HostImpl::destroy()
{
s4u::Host::on_destruction(*this->get_iface());
+ this->get_iface()->on_this_destruction(*this->get_iface());
delete this;
}
void StandardLinkImpl::destroy()
{
s4u::Link::on_destruction(piface_);
+ piface_.on_this_destruction(piface_);
delete this;
}
{
if (not is_on()) {
Resource::turn_on();
- s4u::Link::on_state_change(piface_);
+ s4u::Link::on_onoff(piface_);
+ piface_.on_this_onoff(piface_);
}
}
{
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();
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)
*/
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();
}
}
}
}
-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();
}
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)
**/
/* 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;
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)
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();
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,
vm_state_ = s4u::VirtualMachine::State::RUNNING;
s4u::VirtualMachine::on_resume(*get_iface());
+ get_iface()->on_this_resume(*get_iface());
}
/** @brief Power off a VM.
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
{
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()
/* 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"
/************
* 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)
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);
}
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*/)
{
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);
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();
});
}
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);
});
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());
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
*****************/
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;
});
}
-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.", "",
"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) {
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);
}
delete flow;
flow_from_sock.erase(ns3_socket);
- sink_from_sock.erase(ns3_socket);
}
}
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);
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
#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:
#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);
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)
}
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();
}
}
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)
#include <ns3/node.h>
#include <ns3/tcp-socket-factory.h>
+#include <ns3/udp-socket-factory.h>
#include <ns3/wifi-module.h>
#include <cstdint>
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;
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());
}
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]);
}
}
/* 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 {
/***********
{
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;
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)
{
#include "src/kernel/xml/simgrid_dtd.h"
#include <map>
+#include <memory>
#include <string>
#include <vector>
#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>
"The most recent formalism that this version of SimGrid understands is v4.1.\n"
"Please update your code, or use another, more adapted, file.");
}
+
+static void add_remote_disks()
+{
+ for (auto const& host : simgrid::s4u::Engine::get_instance()->get_all_hosts()) {
+ const char* remote_disk_str = host->get_property("remote_disk");
+ if (not remote_disk_str)
+ continue;
+ std::vector<std::string> tokens;
+ boost::split(tokens, remote_disk_str, boost::is_any_of(":"));
+ simgrid::s4u::Host* remote_host = simgrid::s4u::Host::by_name_or_null(tokens[2]);
+ xbt_assert(remote_host, "You're trying to access a host that does not exist. Please check your platform file");
+
+ const simgrid::s4u::Disk* disk = nullptr;
+ for (auto const& d : remote_host->get_disks())
+ if (d->get_name() == tokens[1]) {
+ disk = d;
+ break;
+ }
+
+ xbt_assert(disk, "You're trying to mount a disk that does not exist. Please check your platform file");
+ host->add_disk(disk);
+
+ XBT_DEBUG("Host '%s' wants to access a remote disk: %s of %s", host->get_cname(), disk->get_cname(),
+ remote_host->get_cname());
+ XBT_DEBUG("Host '%s' now has %zu disks", host->get_cname(), host->get_disks().size());
+ }
+}
+
+static void remove_remote_disks()
+{
+ XBT_DEBUG("Simulation is over, time to unregister remote disks if any");
+ for (auto const& host : simgrid::s4u::Engine::get_instance()->get_all_hosts()) {
+ const char* remote_disk_str = host->get_property("remote_disk");
+ if (remote_disk_str) {
+ std::vector<std::string> tokens;
+ boost::split(tokens, remote_disk_str, boost::is_any_of(":"));
+ XBT_DEBUG("Host '%s' wants to unmount a remote disk: %s of %s", host->get_cname(),
+ tokens[1].c_str(), tokens[2].c_str());
+ host->remove_disk(tokens[1]);
+ XBT_DEBUG("Host '%s' now has %zu disks", host->get_cname(), host->get_disks().size());
+ }
+ }
+}
+
void ETag_simgrid_parse_platform()
{
+ simgrid::s4u::Engine::on_platform_created_cb(&add_remote_disks);
+ simgrid::s4u::Engine::on_simulation_end_cb(&remove_remote_disks);
if (fire_on_platform_created_callback)
simgrid::s4u::Engine::on_platform_created();
+
}
void STag_simgrid_parse_prop()
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) {
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(),
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());
#include "src/kernel/activity/CommImpl.hpp"
#include "src/mc/remote/RemotePtr.hpp"
+#include <algorithm>
#include <exception>
#include <vector>
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 */
}
void mark_done() { this->state_ = InterleavingType::done; }
- inline Transition* get_transition(unsigned times_considered) const
+ /**
+ * @brief Retrieves the transition that we should consider for execution by
+ * this actor from the State instance with respect to which this ActorState object
+ * is considered
+ */
+ std::shared_ptr<Transition> get_transition() const
+ {
+ // The rationale for this selection is as follows:
+ //
+ // 1. For transitions with only one possibility of execution,
+ // we always wish to select action `0` even if we've
+ // marked the transition already as considered (which
+ // we'll do if we explore a trace following that transition).
+ //
+ // 2. For transitions that can be considered multiple
+ // times, we want to be sure to select the most up-to-date
+ // action. In general, this means selecting that which is
+ // now being considered at this state. If, however, we've
+ // executed the
+ //
+ // The formula satisfies both of the above conditions:
+ //
+ // > std::clamp(times_considered_, 0u, max_consider_ - 1)
+ return get_transition(std::clamp(times_considered_, 0u, max_consider_ - 1));
+ }
+
+ std::shared_ptr<Transition> get_transition(unsigned times_considered) const
{
xbt_assert(times_considered < this->pending_transitions_.size(),
"Actor %ld does not have a state available transition with `times_considered = %u`,\n"
"yet one was asked for",
aid_, times_considered);
- return this->pending_transitions_[times_considered].get();
+ return this->pending_transitions_[times_considered];
}
- inline void set_transition(std::shared_ptr<Transition> t, unsigned times_considered)
+ void set_transition(std::shared_ptr<Transition> t, unsigned times_considered)
{
xbt_assert(times_considered < this->pending_transitions_.size(),
"Actor %ld does not have a state available transition with `times_considered = %u`, "
--- /dev/null
+/* 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
--- /dev/null
+/* 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
#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");
* And if we kept it and the actor is enabled in this state, mark the actor as already done, so that
* it is not explored*/
for (auto& [aid, transition] : parent_state_->get_sleep_set()) {
- if (not incoming_transition_->depends(&transition)) {
+ if (not incoming_transition_->depends(transition.get())) {
sleep_set_.try_emplace(aid, transition);
if (strategy_->actors_to_run_.count(aid) != 0) {
XBT_DEBUG("Actor %ld will not be explored, for it is in the sleep set", aid);
-
strategy_->actors_to_run_.at(aid).mark_done();
}
} else
XBT_DEBUG("Transition >>%s<< removed from the sleep set because it was dependent with incoming >>%s<<",
- transition.to_string().c_str(), incoming_transition_->to_string().c_str());
+ transition->to_string().c_str(), incoming_transition_->to_string().c_str());
}
}
}
return strategy_->next_transition();
}
+aid_t State::next_odpor_transition() const
+{
+ return wakeup_tree_.get_min_single_process_actor().value_or(-1);
+}
+
// This should be done in GuidedState, or at least interact with it
std::shared_ptr<Transition> State::execute_next(aid_t next, RemoteApp& app)
{
// 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);
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
#define SIMGRID_MC_STATE_HPP
#include "src/mc/api/ActorState.hpp"
+#include "src/mc/api/ClockVector.hpp"
#include "src/mc/api/RemoteApp.hpp"
#include "src/mc/api/strategy/Strategy.hpp"
+#include "src/mc/explo/odpor/WakeupTree.hpp"
#include "src/mc/transition/Transition.hpp"
#if SIMGRID_HAVE_STATEFUL_MC
/* Sleep sets are composed of the actor and the corresponding transition that made it being added to the sleep
* set. With this information, it is check whether it should be removed from it or not when exploring a new
* transition */
- std::map<aid_t, Transition> sleep_set_;
+ std::map<aid_t, std::shared_ptr<Transition>> sleep_set_;
+
+ /**
+ * The wakeup tree with respect to the execution represented
+ * by the totality of all states before and including this one
+ * and with respect to this state's sleep set
+ */
+ odpor::WakeupTree wakeup_tree_;
+ bool has_initialized_wakeup_tree = false;
public:
explicit State(RemoteApp& remote_app);
/* Returns a positive number if there is another transition to pick, or -1 if not */
aid_t next_transition() const; // this function should disapear as it is redundant with the next one
- /* Same as next_transition, but choice is now guided, and an integer corresponding to the
+ /* Same as next_transition(), but choice is now guided, and an integer corresponding to the
internal cost of the transition is returned */
std::pair<aid_t, int> next_transition_guided() const;
+ /**
+ * Same as next_transition(), but the choice is not based off the ODPOR
+ * wakeup tree associated with this state
+ */
+ aid_t next_odpor_transition() const;
+
/**
* @brief Explore a new path on the remote app; the parameter 'next' must be the result of a previous call to
* next_transition()
/* Marking as TODO some actor in this state:
* + consider_one mark aid actor (and assert it is possible)
- * + consider_best ensure one actor is marked by eventually marking the best regarding its guiding methode
- * + conside_all mark all enabled actor that are not done yet */
+ * + consider_best ensure one actor is marked by eventually marking the best regarding its guiding method
+ * + consider_all mark all enabled actor that are not done yet */
void consider_one(aid_t aid) const { strategy_->consider_one(aid); }
void consider_best() const { strategy_->consider_best(); }
unsigned long consider_all() const { return strategy_->consider_all(); }
Snapshot* get_system_state() const { return system_state_.get(); }
void set_system_state(std::shared_ptr<Snapshot> state) { system_state_ = std::move(state); }
- std::map<aid_t, Transition> const& get_sleep_set() const { return sleep_set_; }
- void add_sleep_set(std::shared_ptr<Transition> t)
+ /**
+ * @brief Computes the backtrack set for this state
+ * according to its definition in Simgrid.
+ *
+ * The backtrack set as it appears in DPOR, SDPOR, and ODPOR
+ * in SimGrid consists of those actors marked as `todo`
+ * (i.e. those that have yet to be explored) as well as those
+ * marked `done` (i.e. those that have already been explored)
+ * since the pseudocode in none of the above algorithms explicitly
+ * removes elements from the backtrack set. DPOR makes use
+ * explicitly of the `done` set, but we again note that the
+ * backtrack set still contains processes added to the done set.
+ */
+ std::unordered_set<aid_t> get_backtrack_set() const;
+ std::unordered_set<aid_t> get_sleeping_actors() const;
+ std::unordered_set<aid_t> get_enabled_actors() const;
+ std::map<aid_t, std::shared_ptr<Transition>> const& get_sleep_set() const { return sleep_set_; }
+ void add_sleep_set(std::shared_ptr<Transition> t) { sleep_set_.insert_or_assign(t->aid_, std::move(t)); }
+ bool is_actor_sleeping(aid_t actor) const
{
- sleep_set_.insert_or_assign(t->aid_, Transition(t->type_, t->aid_, t->times_considered_));
+ return std::find_if(sleep_set_.begin(), sleep_set_.end(), [=](const auto& pair) { return pair.first == actor; }) !=
+ sleep_set_.end();
}
+ /**
+ * @brief Inserts an arbitrary enabled actor into the wakeup tree
+ * associated with this state, if such an actor exists and if
+ * the wakeup tree is already not empty
+ *
+ * @param prior The sequence of steps leading up to this state
+ * with respec to which the tree associated with this state should be
+ * a wakeup tree (wakeup trees are defined relative to an execution)
+ *
+ * @invariant: You should not manipulate a wakeup tree with respect
+ * to more than one execution; doing so will almost certainly lead to
+ * unexpected results as wakeup trees are defined relative to a single
+ * execution
+ */
+ void seed_wakeup_tree_if_needed(const odpor::Execution& prior);
+
+ /**
+ * @brief Initializes the wakeup_tree_ instance by taking the subtree rooted at the
+ * single-process node `N` running actor `p := "actor taken by parent to form this state"`
+ * of the *parent's* wakeup tree
+ */
+ void sprout_tree_from_parent_state();
+
+ /**
+ * @brief Removes the subtree rooted at the single-process node
+ * `N` running actor `p` of this state's wakeup tree
+ */
+ void remove_subtree_using_current_out_transition();
+ bool has_empty_tree() const { return this->wakeup_tree_.empty(); }
+
+ /**
+ * @brief
+ */
+ odpor::WakeupTree::InsertionResult insert_into_wakeup_tree(const odpor::PartialExecution&, const odpor::Execution&);
+
+ /** @brief Prepares the state for re-exploration following
+ * another after having followed ODPOR from this state with
+ * the current out transition
+ *
+ * After ODPOR has completed searching a maximal trace, it
+ * finds the first point in the execution with a nonempty wakeup
+ * tree. This method corresponds to lines 20 and 21 in the ODPOR
+ * pseudocode
+ */
+ void do_odpor_unwind();
+
/* Returns the total amount of states created so far (for statistics) */
static long get_expanded_states() { return expended_states_; }
};
#ifndef SIMGRID_MC_BASICSTRATEGY_HPP
#define SIMGRID_MC_BASICSTRATEGY_HPP
+#include "Strategy.hpp"
+
namespace simgrid::mc {
/** Basic MC guiding class which corresponds to no guide. When asked for different states
/* 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 {
}
*/
-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&) {
#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");
void DFSExplorer::restore_stack(std::shared_ptr<State> state)
{
stack_.clear();
+ execution_seq_ = odpor::Execution();
auto current_state = state;
stack_.emplace_front(current_state);
// condition corresponds to reaching initial state
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
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;
}
}
#endif
+ if (reduction_mode_ == ReductionMode::odpor) {
+ // In the case of ODPOR, the wakeup tree for this
+ // state may be empty if we're exploring new territory
+ // (rather than following the partial execution of a
+ // wakeup tree). This corresponds to lines 9 to 13 of
+ // the ODPOR pseudocode
+ //
+ // INVARIANT: The execution sequence should be consistent
+ // with the state when seeding the tree. If the sequence
+ // gets out of sync with the state, selection will not
+ // work as we intend
+ state->seed_wakeup_tree_if_needed(execution_seq_);
+ }
+
// Search for the next transition
- // next_transition returns a pair<aid_t, int> in case we want to consider multiple state (eg. during backtrack)
- auto [next, _] = state->next_transition_guided();
+ // next_transition returns a pair<aid_t, int>
+ // in case we want to consider multiple states (eg. during backtrack)
+ const aid_t next = reduction_mode_ == ReductionMode::odpor ? state->next_odpor_transition()
+ : std::get<0>(state->next_transition_guided());
if (next < 0) { // If there is no more transition in the current state, backtrack.
XBT_VERB("%lu actors remain, but none of them need to be interleaved (depth %zu).", state->get_actor_count(),
if (_sg_mc_sleep_set && XBT_LOG_ISENABLED(mc_dfs, xbt_log_priority_verbose)) {
XBT_VERB("Sleep set actually containing:");
for (auto& [aid, transition] : state->get_sleep_set())
- XBT_VERB(" <%ld,%s>", aid, transition.to_string().c_str());
+ XBT_VERB(" <%ld,%s>", aid, transition->to_string().c_str());
}
/* Actually answer the request: let's execute the selected request (MCed does one step) */
- state->execute_next(next, get_remote_app());
+ const auto executed_transition = state->execute_next(next, get_remote_app());
on_transition_execute_signal(state->get_transition_out().get(), get_remote_app());
// If there are processes to interleave and the maximum depth has not been
auto next_state = std::make_shared<State>(get_remote_app(), state);
on_state_creation_signal(next_state.get(), get_remote_app());
- /* Sleep set procedure:
- * adding the taken transition to the sleep set of the original state.
- * <!> Since the parent sleep set is used to compute the child sleep set, this need to be
- * done after next_state creation */
- XBT_DEBUG("Marking Transition >>%s<< of process %ld done and adding it to the sleep set",
- state->get_transition_out()->to_string().c_str(), state->get_transition_out()->aid_);
- state->add_sleep_set(state->get_transition_out()); // Actors are marked done when they are considerd in ActorState
+ if (reduction_mode_ == ReductionMode::odpor) {
+ // With ODPOR, after taking a step forward, we must
+ // assign a copy of that subtree to the next state.
+ //
+ // NOTE: We only add actions to the sleep set AFTER
+ // we've regenerated states. We must perform the search
+ // fully down a single path before we consider adding
+ // any elements to the sleep set according to the pseudocode
+ next_state->sprout_tree_from_parent_state();
+ } else {
+ /* Sleep set procedure:
+ * adding the taken transition to the sleep set of the original state.
+ * <!> Since the parent sleep set is used to compute the child sleep set, this need to be
+ * done after next_state creation */
+ XBT_DEBUG("Marking Transition >>%s<< of process %ld done and adding it to the sleep set",
+ state->get_transition_out()->to_string().c_str(), state->get_transition_out()->aid_);
+ state->add_sleep_set(
+ state->get_transition_out()); // Actors are marked done when they are considered in ActorState
+ }
/* DPOR persistent set procedure:
* for each new transition considered, check if it depends on any other previous transition executed before it
}
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
this->check_non_termination(next_state.get());
#if SIMGRID_HAVE_STATEFUL_MC
- /* Check whether we already explored next_state in the past (but only if interested in state-equality reduction) */
+ /* Check whether we already explored next_state in the past (but only if interested in state-equality reduction)
+ */
if (_sg_mc_max_visited_states > 0)
visited_state_ = visited_states_.addVisitedState(next_state->get_num(), next_state.get(), get_remote_app());
#endif
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());
get_remote_app().check_deadlock();
// Take the point with smallest distance
- auto backtracking_point = best_opened_state();
+ auto backtracking_point = reduction_mode_ == ReductionMode::odpor ? next_odpor_state() : best_opened_state();
// if no backtracking point, then set the stack_ to empty so we can end the exploration
if (not backtracking_point) {
this->restore_stack(backtracking_point);
}
-DFSExplorer::DFSExplorer(const std::vector<char*>& args, bool with_dpor, bool need_memory_info)
+DFSExplorer::DFSExplorer(const std::vector<char*>& args, ReductionMode mode, bool need_memory_info)
: Exploration(args, need_memory_info || _sg_mc_termination
#if SIMGRID_HAVE_STATEFUL_MC
|| _sg_mc_checkpoint > 0
#endif
- )
+ )
+ , reduction_mode_(mode)
{
- if (with_dpor)
- reduction_mode_ = ReductionMode::dpor;
- else
- reduction_mode_ = ReductionMode::none;
-
if (_sg_mc_termination) {
- if (with_dpor) {
+ if (mode != ReductionMode::none) {
XBT_INFO("Check non progressive cycles (turning DPOR off)");
reduction_mode_ = ReductionMode::none;
} else {
}
if (stack_.back()->count_todo_multiples() > 1)
opened_states_.emplace_back(stack_.back());
+
+ if (mode == ReductionMode::odpor && !_sg_mc_sleep_set) {
+ // ODPOR requires the use of sleep sets; SDPOR
+ // "likes" using sleep sets but it is not strictly
+ // required
+ XBT_INFO("Forcing the use of sleep sets for use with ODPOR");
+ _sg_mc_sleep_set = true;
+ }
}
-Exploration* create_dfs_exploration(const std::vector<char*>& args, bool with_dpor)
+Exploration* create_dfs_exploration(const std::vector<char*>& args, ReductionMode mode)
{
- return new DFSExplorer(args, with_dpor);
+ return new DFSExplorer(args, mode);
}
} // namespace simgrid::mc
#ifndef SIMGRID_MC_SAFETY_CHECKER_HPP
#define SIMGRID_MC_SAFETY_CHECKER_HPP
+#include "src/mc/api/ClockVector.hpp"
#include "src/mc/api/State.hpp"
#include "src/mc/explo/Exploration.hpp"
+#include "src/mc/explo/odpor/Execution.hpp"
+#include "src/mc/mc_config.hpp"
#if SIMGRID_HAVE_STATEFUL_MC
#include "src/mc/VisitedState.hpp"
#endif
+#include <deque>
#include <list>
#include <memory>
#include <set>
#include <string>
+#include <unordered_map>
#include <vector>
namespace simgrid::mc {
-using stack_t = std::list<std::shared_ptr<State>>;
+using stack_t = std::deque<std::shared_ptr<State>>;
class XBT_PRIVATE DFSExplorer : public Exploration {
- XBT_DECLARE_ENUM_CLASS(ReductionMode, none, dpor);
-
+private:
ReductionMode reduction_mode_;
unsigned long backtrack_count_ = 0; // for statistics
unsigned long visited_states_count_ = 0; // for statistics
static xbt::signal<void(RemoteApp&)> on_log_state_signal;
public:
- explicit DFSExplorer(const std::vector<char*>& args, bool with_dpor, bool need_memory_info = false);
+ explicit DFSExplorer(const std::vector<char*>& args, ReductionMode mode, bool need_memory_info = false);
void run() override;
RecordTrace get_record_trace() override;
void log_state() override;
/** 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_;
std::vector<std::shared_ptr<State>> opened_states_;
std::shared_ptr<State> best_opened_state();
+ /** If we're running ODPOR, picks the corresponding state in the stack
+ * (opened_states_ are ignored)
+ */
+ std::shared_ptr<State> next_odpor_state();
+
/** Change current stack_ value to correspond to the one we would have
* had if we executed transition to get to state. This is required when
* backtracking, and achieved thanks to the fact states save their parent.*/
#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>
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 */
// 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
#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"
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved. */
+
+/* This program is free software; you can redistribute it and/or modify it
+ * under the terms of the license (GNU LGPL) which comes with this package. */
+
+#ifndef SIMGRID_MC_ODPOR_EXECUTION_HPP
+#define SIMGRID_MC_ODPOR_EXECUTION_HPP
+
+#include "src/mc/api/ClockVector.hpp"
+#include "src/mc/explo/odpor/odpor_forward.hpp"
+#include "src/mc/mc_forward.hpp"
+#include "src/mc/mc_record.hpp"
+#include "src/mc/transition/Transition.hpp"
+
+#include <list>
+#include <optional>
+#include <unordered_set>
+#include <vector>
+
+namespace simgrid::mc::odpor {
+
+std::vector<std::string> get_textual_trace(const PartialExecution& w);
+
+/**
+ * @brief The occurrence of a transition in an execution
+ *
+ * An execution is set of *events*, where each element represents
+ * the occurrence or execution of the `i`th step of a particular
+ * actor `j`
+ */
+class Event {
+ std::pair<std::shared_ptr<Transition>, ClockVector> contents_;
+
+public:
+ Event() = default;
+ Event(Event&&) = default;
+ Event(const Event&) = default;
+ Event& operator=(const Event&) = default;
+ explicit Event(std::pair<std::shared_ptr<Transition>, ClockVector> pair) : contents_(std::move(pair)) {}
+
+ std::shared_ptr<Transition> get_transition() const { return std::get<0>(contents_); }
+ const ClockVector& get_clock_vector() const { return std::get<1>(contents_); }
+};
+
+/**
+ * @brief An ordered sequence of transitions which describe
+ * the evolution of a process undergoing model checking
+ *
+ * An execution conceptually is just a string of actors
+ * ids (e.g. "1.2.3.1.2.2.1.1"), where the `i`th occurrence
+ * of actor id `j` corresponds to the `i`th action executed
+ * by the actor with id `j` (viz. the `i`th step of actor `j`).
+ * Executions can stand alone on their own or can extend
+ * the execution of other sequences
+ *
+ * Executions are conceived based on the following papers:
+ * 1. "Source Sets: A Foundation for Optimal Dynamic Partial Order Reduction"
+ * by Abdulla et al.
+ *
+ * In addition to representing an actual steps taken,
+ * an execution keeps track of the "happens-before"
+ * relation among the transitions in the execution
+ * by following the procedure outlined in section 4 of the
+ * original DPOR paper with clock vectors.
+ * As new transitions are added to the execution, clock vectors are
+ * computed as appropriate and associated with the corresponding position
+ * in the execution. This allows us to determine “happens-before” in
+ * constant-time between points in the execution (called events
+ * [which is unfortunately the same name used in UDPOR for a slightly
+ * different concept]), albeit for an up-front cost of traversing the
+ * execution stack. The happens-before relation is important in many
+ * places in SDPOR and ODPOR.
+ *
+ * @note: For more nuanced happens-before relations, clock
+ * vectors may not always suffice. Clock vectors work
+ * well with transition-based dependencies like that used in
+ * SimGrid; but to have a more refined independence relation,
+ * an event-based dependency approach is needed. See the section 2
+ * in the ODPOR paper [1] concerning event-based dependencies and
+ * how the happens-before relation can be refined in a
+ * computation model much like that of SimGrid. In fact, the same issue
+ * arrises with UDPOR with context-sensitive dependencies:
+ * the two concepts are analogous if not identical
+ */
+class Execution {
+private:
+ std::vector<Event> contents_;
+ Execution(std::vector<Event>&& contents) : contents_(std::move(contents)) {}
+
+public:
+ using EventHandle = uint32_t;
+
+ Execution() = default;
+ Execution(const Execution&) = default;
+ Execution& operator=(Execution const&) = default;
+ Execution(Execution&&) = default;
+ Execution(const PartialExecution&);
+
+ std::vector<std::string> get_textual_trace() const;
+
+ size_t size() const { return this->contents_.size(); }
+ bool empty() const { return this->contents_.empty(); }
+ auto begin() const { return this->contents_.begin(); }
+ auto end() const { return this->contents_.end(); }
+
+ /**
+ * @brief Computes the "core" portion the SDPOR algorithm,
+ * viz. the intersection of the backtracking set and the
+ * set of initials with respect to the *last* event added
+ * to the execution
+ *
+ * The "core" portion of the SDPOR algorithm is found on
+ * lines 6-9 of the pseudocode:
+ *
+ * 6 | let E' := pre(E, e)
+ * 7 | let v := notdep(e, E).p
+ * 8 | if I_[E'](v) ∩ backtrack(E') = empty then
+ * 9 | --> add some q in I_[E'](v) to backtrack(E')
+ *
+ * This method computes all of the lines simultaneously,
+ * returning some actor `q` if it passes line 8 and exists.
+ * The event `e` and the set `backtrack(E')` are the provided
+ * arguments to the method.
+ *
+ * @param e the event with respect to which to determine
+ * whether a backtrack point needs to be added for the
+ * prefix corresponding to the execution prior to `e`
+ *
+ * @param backtrack_set The set of actors which should
+ * not be considered for selection as an SDPOR initial.
+ * While this set need not necessarily correspond to the
+ * backtrack set `backtrack(E')`, doing so provides what
+ * is expected for SDPOR
+ *
+ * See the SDPOR algorithm pseudocode in [1] for more
+ * details for the context of the function.
+ *
+ * @invariant: This method assumes that events `e` and
+ * `e' := get_latest_event_handle()` are in a *reversible* race
+ * as is explicitly the case in SDPOR
+ *
+ * @returns an actor not contained in `disqualified` which
+ * can serve as an initial to reverse the race between `e`
+ * and `e'`
+ */
+ std::unordered_set<aid_t> get_missing_source_set_actors_from(EventHandle e,
+ const std::unordered_set<aid_t>& backtrack_set) const;
+
+ /**
+ * @brief Computes the analogous lines from the SDPOR algorithm
+ * in the ODPOR algorithm, viz. the intersection of the slee set
+ * and the set of weak initials with respect to the given pair
+ * of racing events
+ *
+ * This method computes lines 4-6 of the ODPOR pseudocode, viz.:
+ *
+ * 4 | let E' := pre(E, e)
+ * 5 | let v := notdep(e, E).e'^
+ * 6 | if sleep(E') ∩ WI_[E'](v) = empty then ...
+ *
+ * The sequence `v` is computed and returned as needed, based on whether
+ * the check on line 6 passes.
+ *
+ * @invariant: This method assumes that events `e` and
+ * `e_prime` are in a *reversible* race as is the case
+ * in ODPOR
+ */
+ std::optional<PartialExecution> get_odpor_extension_from(EventHandle e, EventHandle e_prime,
+ const State& state_at_e) const;
+
+ /**
+ * @brief For a given sequence of actors `v` and a sequence of transitions `w`,
+ * computes the sequence, if any, that should be inserted as a child in wakeup tree for
+ * this execution
+ *
+ * Recall that the procedure for implementing the insertion
+ * is outlined in section 6.2 of Abdulla et al. 2017 as follows:
+ *
+ * | Let `v` be the smallest (w.r.t to "<") sequence in [the tree] B
+ * | such that `v ~_[E] w`. If `v` is a leaf node, the tree can be left
+ * | unmodified.
+ * |
+ * | Otherwise let `w'` be the shortest sequence such that `w [=_[E] v.w'`
+ * | and add `v.w'` as a new leaf, ordered after all already existing nodes
+ * | of the form `v.w''`
+ *
+ * This method computes the result `v.w'` as needed (viz. only if `v ~_[E] w`
+ * with respect to this execution `E`)
+ *
+ * The procedure for determining `v ~_[E] w` is given as Lemma 4.6 of
+ * Abdulla et al. 2017:
+ *
+ * | The relation `v ~_[E] w` holds if either
+ * | (1) v = <>, or
+ * | (2) v := p.v' and either
+ * | (a) p in I_[E](w) and `v' ~_[E.p] (w \ p)`
+ * | (b) E ⊢ p ◊ w and `v' ~_[E.p] w`
+ *
+ * @invariant: This method assumes that `E.v` is a valid execution, viz.
+ * that the events of `E` are sufficient to enabled `v_0` and that
+ * `v_0, ..., v_{i - 1}` are sufficient to enable `v_i`. This is the
+ * case when e.g. `v := notdep(e, E).p` for example in ODPOR
+ *
+ * @returns a partial execution `w'` that should be inserted
+ * as a child of a wakeup tree node with the associated sequence `v`.
+ */
+ std::optional<PartialExecution> get_shortest_odpor_sq_subset_insertion(const PartialExecution& v,
+ const PartialExecution& w) const;
+
+ /**
+ * @brief For a given sequence `w`, determines whether p in I_[E](w)
+ *
+ * @note: You may notice that some of the other methods compute this
+ * value as well. What we notice, though, in those cases is that
+ * we are repeatedly asking about initials with respect to an execution.
+ * It is better, then, to bunch the work together in those cases to
+ * get asymptotically better results (e.g. instead of calling with all
+ * `N` actors, we can process them "in-parallel" as is done with the
+ * computation of SDPOR initials)
+ */
+ bool is_initial_after_execution_of(const PartialExecution& w, aid_t p) const;
+
+ /**
+ * @brief Determines whether `E ⊢ p ◊ w` given the next action taken by `p`
+ */
+ bool is_independent_with_execution_of(const PartialExecution& w, std::shared_ptr<Transition> next_E_p) const;
+
+ /**
+ * @brief Determines the event associated with
+ * the given handle `handle`
+ */
+ const Event& get_event_with_handle(EventHandle handle) const { return contents_[handle]; }
+
+ /**
+ * @brief Determines the actor associated with
+ * the given event handle `handle`
+ */
+ aid_t get_actor_with_handle(EventHandle handle) const { return get_event_with_handle(handle).get_transition()->aid_; }
+
+ /**
+ * @brief Determines the transition associated with the given handle `handle`
+ */
+ const Transition* get_transition_for_handle(EventHandle handle) const
+ {
+ return get_event_with_handle(handle).get_transition().get();
+ }
+
+ /**
+ * @brief Returns a handle to the newest event of the execution,
+ * if such an event exists
+ */
+ std::optional<EventHandle> get_latest_event_handle() const
+ {
+ return contents_.empty() ? std::nullopt : std::optional<EventHandle>{static_cast<EventHandle>(size() - 1)};
+ }
+
+ /**
+ * @brief Returns a set of events which are in
+ * "immediate conflict" (according to the definition given
+ * in the ODPOR paper) with the given event
+ *
+ * Two events `e` and `e'` in an execution `E` are said to
+ * race iff
+ *
+ * 1. `proc(e) != proc(e')`; that is, the events correspond to
+ * the execution of different actors
+ * 2. `e -->_E e'` and there is no `e''` in `E` such that
+ * `e -->_E e''` and `e'' -->_E e'`; that is, the two events
+ * "happen-before" one another in `E` and no other event in
+ * `E` "happens-between" `e` and `e'`
+ *
+ * @param handle the event with respect to which races are
+ * computed
+ * @returns a set of event handles from which race with `handle`
+ */
+ std::unordered_set<EventHandle> get_racing_events_of(EventHandle handle) const;
+
+ /**
+ * @brief Returns a set of events which are in a reversible
+ * race with the given event handle `handle`
+ *
+ * Two events `e` and `e'` in an execution `E` are said to
+ * be in a reversible race iff
+ *
+ * 1. `e` and `e'` race
+ * 2. In any equivalent execution sequence `E'` to `E`
+ * where `e` occurs immediately before `e'`, the actor
+ * running `e'` was enabled in the state prior to `e`
+ *
+ * @param handle the event with respect to which
+ * reversible races are computed
+ * @returns a set of event handles from which are in a reversible
+ * race with `handle`
+ */
+ std::unordered_set<EventHandle> get_reversible_races_of(EventHandle handle) const;
+
+ /**
+ * @brief Computes `pre(e, E)` as described in ODPOR [1]
+ *
+ * The execution `pre(e, E)` for an event `e` in an
+ * execution `E` is the contiguous prefix of events
+ * `E' <= E` up to by excluding the event `e` itself.
+ * The prefix intuitively represents the "history" of
+ * causes that permitted event `e` to exist (roughly
+ * speaking)
+ */
+ Execution get_prefix_before(EventHandle) const;
+
+ /**
+ * @brief Whether the event represented by `e1`
+ * "happens-before" the event represented by
+ * `e2` in the context of this execution
+ *
+ * In the terminology of the ODPOR paper,
+ * this function computes
+ *
+ * `e1 --->_E e2`
+ *
+ * where `E` is this execution
+ *
+ * @note: The happens-before relation computed by this
+ * execution is "coarse" in the sense that context-sensitive
+ * independence is not exploited. To include such context-sensitive
+ * dependencies requires a new method of keeping track of
+ * the happens-before procedure, which is nontrivial...
+ */
+ bool happens_before(EventHandle e1, EventHandle e2) const;
+
+ /**
+ * @brief Extends the execution by one more step
+ *
+ * Intutively, pushing a transition `t` onto execution `E`
+ * is equivalent to making the execution become (using the
+ * notation of [1]) `E.proc(t)` where `proc(t)` is the
+ * actor which executed transition `t`.
+ */
+ void push_transition(std::shared_ptr<Transition>);
+
+ /**
+ * @brief Extends the execution by a sequence of steps
+ */
+ void push_partial_execution(const PartialExecution&);
+};
+
+} // namespace simgrid::mc::odpor
+#endif
--- /dev/null
+/* 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{});
+ }
+ }
+}
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved. */
+
+/* This program is free software; you can redistribute it and/or modify it
+ * under the terms of the license (GNU LGPL) which comes with this package. */
+
+#ifndef SIMGRID_MC_ODPOR_WAKEUP_TREE_HPP
+#define SIMGRID_MC_ODPOR_WAKEUP_TREE_HPP
+
+#include "src/mc/explo/odpor/WakeupTreeIterator.hpp"
+#include "src/mc/explo/odpor/odpor_forward.hpp"
+#include "src/mc/transition/Transition.hpp"
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace simgrid::mc::odpor {
+
+/**
+ * @brief A single node in a wakeup tree
+ *
+ * Each node in a wakeup tree contains
+ */
+class WakeupTreeNode {
+private:
+ explicit WakeupTreeNode(std::shared_ptr<Transition> u) : action_(u) {}
+
+ WakeupTreeNode* parent_ = nullptr;
+
+ /** An ordered list of children of for this node in the tree */
+ std::list<WakeupTreeNode*> children_;
+
+ /** @brief The contents of the node */
+ std::shared_ptr<Transition> action_;
+
+ /** @brief Removes the node as a child from the parent */
+ void detatch_from_parent();
+
+ /** Allows the owning tree to insert directly into the child */
+ friend WakeupTree;
+ friend WakeupTreeIterator;
+
+public:
+ ~WakeupTreeNode() = default;
+ WakeupTreeNode(const WakeupTreeNode&) = delete;
+ WakeupTreeNode(WakeupTreeNode&&) = default;
+ WakeupTreeNode& operator=(const WakeupTreeNode&) = delete;
+ WakeupTreeNode& operator=(WakeupTreeNode&&) = default;
+
+ auto begin() const { return this->children_.begin(); }
+ auto end() const { return this->children_.end(); }
+ auto rbegin() const { return this->children_.rbegin(); }
+ auto rend() const { return this->children_.rend(); }
+
+ bool is_leaf() const { return children_.empty(); }
+ bool is_root() const { return parent_ == nullptr; }
+ aid_t get_actor() const { return action_->aid_; }
+ PartialExecution get_sequence() const;
+ std::shared_ptr<Transition> get_action() const { return action_; }
+ const std::list<WakeupTreeNode*>& get_ordered_children() const { return children_; }
+
+ /** Insert a node `node` as a new child of this node */
+ void add_child(WakeupTreeNode* node);
+};
+
+/**
+ * @brief The structure used by ODPOR to maintains paths of execution
+ * that should be followed in the future
+ *
+ * The wakeup tree data structure is formally defined in the Abdulla et al.
+ * 2017 ODPOR paper. Conceptually, the tree consists of nodes which are
+ * mapped to actions. Each node represents a partial extension of an execution,
+ * the complete extension being the transitions taken in sequence from
+ * the root of the tree to the node itself. Leaf nodes in the tree conceptually,
+ * then, represent paths that are guaranteed to explore different parts
+ * of the search space.
+ *
+ * Iteration over a wakeup tree occurs as a post-order traversal of its nodes
+ *
+ * @note A wakeup tree is defined relative to some execution `E`. The
+ * structure itself does not hold onto a reference of the execution with
+ * respect to which it is a wakeup tree.
+ *
+ * @todo: If the idea of execution "views" is ever added -- viz. being able
+ * to share the contents of a single execution -- then a wakeup tree could
+ * contain a reference to such a view which would then be maintained by the
+ * manipulator of the tree
+ */
+class WakeupTree {
+private:
+ WakeupTreeNode* root_;
+
+ /**
+ * @brief All of the nodes that are currently are a part of the tree
+ *
+ * @invariant Each node event maps itself to the owner of that node,
+ * i.e. the unique pointer that manages the data at the address. The tree owns all
+ * of the addresses that are referenced by the nodes WakeupTreeNode.
+ * ODPOR guarantees that nodes are persisted as long as needed.
+ */
+ std::unordered_map<WakeupTreeNode*, std::unique_ptr<WakeupTreeNode>> nodes_;
+
+ void insert_node(std::unique_ptr<WakeupTreeNode> node);
+ void insert_sequence_after(WakeupTreeNode* node, const PartialExecution& w);
+ void remove_node(WakeupTreeNode* node);
+ bool contains(WakeupTreeNode* node) const;
+
+ /**
+ * @brief Removes the node `root` and all of its descendants from
+ * this wakeup tree
+ *
+ * @throws: If the node `root` is not contained in this tree, an
+ * exception is raised
+ */
+ void remove_subtree_rooted_at(WakeupTreeNode* root);
+
+ /**
+ * @brief Adds a new node to the tree, disconnected from
+ * any other, which represents the partial execution
+ * "fragment" `u`
+ */
+ WakeupTreeNode* make_node(std::shared_ptr<Transition> u);
+
+ /* Allow the iterator to access the contents of the tree */
+ friend WakeupTreeIterator;
+
+public:
+ WakeupTree();
+ explicit WakeupTree(std::unique_ptr<WakeupTreeNode> root);
+
+ /**
+ * @brief Creates a copy of the subtree whose root is the node
+ * `root` in this tree
+ */
+ static WakeupTree make_subtree_rooted_at(WakeupTreeNode* root);
+
+ auto begin() const { return WakeupTreeIterator(*this); }
+ auto end() const { return WakeupTreeIterator(); }
+
+ std::vector<std::string> get_single_process_texts() const;
+
+ /**
+ * @brief Remove the subtree of the smallest (with respect
+ * to the tree's "<" relation) single-process node.
+ *
+ * A "single-process" node is one whose execution represents
+ * taking a single action (i.e. those of the root node). The
+ * smallest under "<" is that which is continuously selected and
+ * removed by ODPOR.
+ *
+ * If the tree is empty, this method has no effect.
+ */
+ void remove_min_single_process_subtree();
+
+ /**
+ * @brief Whether or not this tree is considered empty
+ *
+ * @note Unlike other collection types, a wakeup tree is
+ * considered "empty" if it only contains the root node;
+ * that is, if it is "uninteresting". In such a case,
+ */
+ bool empty() const { return nodes_.size() == static_cast<size_t>(1); }
+
+ /**
+ * @brief Returns the number of *non-empty* entries in the tree, viz. the
+ * number of nodes in the tree that have an action mapped to them
+ */
+ size_t get_num_entries() const { return !empty() ? (nodes_.size() - 1) : static_cast<size_t>(0); }
+
+ /**
+ * @brief Returns the number of nodes in the tree, including the root node
+ */
+ size_t get_num_nodes() const { return nodes_.size(); }
+
+ /**
+ * @brief Gets the actor of the node that is the "smallest" (with respect
+ * to the tree's "<" relation) single-process node.
+ *
+ * If the tree is empty, returns std::nullopt
+ */
+ std::optional<aid_t> get_min_single_process_actor() const;
+
+ /**
+ * @brief Gets the node itself that is the "smallest" (with respect
+ * to the tree's "<" relation) single-process node.
+ *
+ * If the tree is empty, returns std::nullopt
+ */
+ std::optional<WakeupTreeNode*> get_min_single_process_node() const;
+
+ /** @brief Describes how a tree insertion was carried out */
+ enum class InsertionResult { leaf, interior_node, root };
+
+ /**
+ * @brief Inserts an sequence `seq` of processes into the tree
+ * such that that this tree is a wakeup tree relative to the
+ * given execution
+ *
+ * A key component of managing wakeup trees in ODPOR is
+ * determining what should be inserted into a wakeup tree.
+ * The procedure for implementing the insertion is outlined in section 6.2
+ * of Abdulla et al. 2017 as follows:
+ *
+ * | Let `v` be the smallest (w.r.t to "<") sequence in [the tree] B
+ * | such that `v ~_[E] w`. If `v` is a leaf node, the tree can be left
+ * | unmodified.
+ * |
+ * | Otherwise let `w'` be the shortest sequence such that `w [=_[E] v.w'`
+ * | and add `v.w'` as a new leaf, ordered after all already existing nodes
+ * | of the form `v.w''`
+ *
+ * This method performs the post-order search of part one and the insertion of
+ * `v.w'` of part two of the above procedure. Note that the execution will
+ * provide `v.w'` (see `Execution::get_shortest_odpor_sq_subset_insertion()`).
+ *
+ * @invariant: It is assumed that this tree is a wakeup tree
+ * with respect to the given execution `E`
+ *
+ * @return Whether a sequence equivalent to `seq` is already contained
+ * as a leaf node in the tree
+ */
+ InsertionResult insert(const Execution& E, const PartialExecution& seq);
+};
+
+} // namespace simgrid::mc::odpor
+#endif
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved. */
+
+/* This program is free software; you can redistribute it and/or modify it
+ * under the terms of the license (GNU LGPL) which comes with this package. */
+
+/** @file odpor_forward.hpp
+ *
+ * Forward definitions for MC types specific to ODPOR
+ */
+
+#ifndef SIMGRID_MC_ODPOR_FORWARD_HPP
+#define SIMGRID_MC_ODPOR_FORWARD_HPP
+
+#include "src/mc/mc_forward.hpp"
+#include <list>
+#include <memory>
+#include <simgrid/forward.h>
+
+namespace simgrid::mc::odpor {
+
+using PartialExecution = std::list<std::shared_ptr<Transition>>;
+
+class Event;
+class Execution;
+class ReversibleRaceCalculator;
+class WakeupTree;
+class WakeupTreeNode;
+class WakeupTreeIterator;
+
+} // namespace simgrid::mc::odpor
+
+namespace simgrid::mc {
+
+// Permit ODPOR or SDPOR to be used as namespaces
+// Many of the structures overlap, so it doesn't
+// make sense to some in one and not the other.
+// Having one for each algorithm makes the corresponding
+// code easier to read
+namespace sdpor = simgrid::mc::odpor;
+
+} // namespace simgrid::mc
+
+#endif
--- /dev/null
+/* 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
#if SIMGRID_HAVE_STATEFUL_MC
if (_sg_mc_comms_determinism || _sg_mc_send_determinism)
- explo = std::unique_ptr<Exploration>(create_communication_determinism_checker(argv_copy, cfg_use_DPOR()));
+ explo = std::unique_ptr<Exploration>(
+ create_communication_determinism_checker(argv_copy, get_model_checking_reduction()));
else if (_sg_mc_unfolding_checker)
explo = std::unique_ptr<Exploration>(create_udpor_checker(argv_copy));
else if (not _sg_mc_property_file.get().empty())
explo = std::unique_ptr<Exploration>(create_liveness_checker(argv_copy));
else
#endif
- explo = std::unique_ptr<Exploration>(create_dfs_exploration(argv_copy, cfg_use_DPOR()));
+ explo = std::unique_ptr<Exploration>(create_dfs_exploration(argv_copy, get_model_checking_reduction()));
ExitStatus status;
try {
{
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) {
{
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) {
REQUIRE(alternative.value().get_events() == EventSet({e0_handle, e7_handle, e9_handle}));
}
}
-}
\ No newline at end of file
+}
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{
"model-check/termination", "Whether to enable non progressive cycle detection", false,
[](bool) { _mc_cfg_cb_check("value to enable/disable the detection of non progressive cycles"); }};
-bool simgrid::mc::cfg_use_DPOR()
+simgrid::mc::ReductionMode simgrid::mc::get_model_checking_reduction()
{
- if (cfg_mc_reduction.get() == "dpor" && _sg_mc_max_visited_states__ > 0) {
+ if ((cfg_mc_reduction.get() == "dpor" || cfg_mc_reduction.get() == "sdpor" || cfg_mc_reduction.get() == "odpor") &&
+ _sg_mc_max_visited_states__ > 0) {
XBT_INFO("Disabling DPOR since state-equality reduction is activated with 'model-check/visited'");
- return false;
+ return simgrid::mc::ReductionMode::none;
+ }
+
+ if (cfg_mc_reduction.get() == "none") {
+ return ReductionMode::none;
+ } else if (cfg_mc_reduction.get() == "dpor") {
+ return ReductionMode::dpor;
+ } else if (cfg_mc_reduction.get() == "sdpor") {
+ return ReductionMode::sdpor;
+ } else if (cfg_mc_reduction.get() == "odpor") {
+ return ReductionMode::odpor;
+ } else if (cfg_mc_reduction.get() == "udpor") {
+ XBT_INFO("No reduction will be used: "
+ "UDPOR has a dedicated invocation 'model-check/unfolding-checker' "
+ "but is not yet fully supported in SimGrid");
+ return ReductionMode::none;
+ } else {
+ XBT_INFO("Unknown reduction mode: defaulting to no reduction");
+ return ReductionMode::none;
}
- return cfg_mc_reduction.get() == "dpor";
}
/********************************** Configuration of MC **************************************/
namespace simgrid::mc {
-bool cfg_use_DPOR(); // "model-check/reduction" == "DPOR"
+XBT_DECLARE_ENUM_CLASS(ReductionMode, none, dpor, sdpor, odpor);
XBT_DECLARE_ENUM_CLASS(ModelCheckingMode, NONE, APP_SIDE, CHECKER_SIDE, REPLAY);
+ReductionMode get_model_checking_reduction(); // "model-check/reduction" == "DPOR"
XBT_PUBLIC ModelCheckingMode get_model_checking_mode();
XBT_PUBLIC void set_model_checking_mode(ModelCheckingMode mode);
-};
+}; // namespace simgrid::mc
extern XBT_PUBLIC simgrid::config::Flag<std::string> _sg_mc_buffering;
extern XBT_PRIVATE simgrid::config::Flag<int> _sg_mc_checkpoint;
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); }
#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();
}
#include <sys/mman.h>
#include <xbt/random.hpp>
-/**************** Class BOOST_tests *************************/
-using simgrid::mc::Region;
class snap_test_helper {
- static simgrid::mc::PageStore page_store_;
- static std::unique_ptr<simgrid::mc::RemoteProcessMemory> memory_;
+ simgrid::mc::PageStore page_store_{500};
+ simgrid::mc::RemoteProcessMemory memory_{getpid(), nullptr};
-public:
- static void init_memory(void* mem, size_t size);
- static void Init();
struct prologue_return {
size_t size;
- void* src;
- void* dstn;
- std::unique_ptr<Region> region0;
- std::unique_ptr<Region> region;
+ std::byte* src;
+ std::byte* dstn;
+ std::unique_ptr<simgrid::mc::Region> region0;
+ std::unique_ptr<simgrid::mc::Region> region;
};
- static prologue_return prologue(int n); // common to the below 5 fxs
- static void read_whole_region();
- static void read_region_parts();
- static void compare_whole_region();
- static void compare_region_parts();
- static void read_pointer();
-
- static void cleanup() { memory_ = nullptr; }
-};
+ prologue_return prologue(int n); // common to the below 5 fxs
+
+ static void init_memory(std::byte* mem, size_t size);
+
+public:
+ void read_whole_region();
+ void read_region_parts();
+ void compare_whole_region();
+ void compare_region_parts();
+ void read_pointer();
-// static member variables init.
-simgrid::mc::PageStore snap_test_helper::page_store_(500);
-std::unique_ptr<simgrid::mc::RemoteProcessMemory> snap_test_helper::memory_ = nullptr;
+ static void basic_requirements();
+};
-void snap_test_helper::init_memory(void* mem, size_t size)
+void snap_test_helper::init_memory(std::byte* mem, size_t size)
{
- auto* dest = static_cast<char*>(mem);
- for (size_t i = 0; i < size; ++i) {
- dest[i] = simgrid::xbt::random::uniform_int(0, 0xff);
- }
+ std::generate_n(mem, size, []() { return static_cast<std::byte>(simgrid::xbt::random::uniform_int(0, 0xff)); });
}
-void snap_test_helper::Init()
+void snap_test_helper::basic_requirements()
{
REQUIRE(xbt_pagesize == getpagesize());
REQUIRE(1 << xbt_pagebits == xbt_pagesize);
-
- memory_ = std::make_unique<simgrid::mc::RemoteProcessMemory>(getpid(), nullptr);
}
snap_test_helper::prologue_return snap_test_helper::prologue(int n)
{
// Store region page(s):
size_t byte_size = n * xbt_pagesize;
- void* source = mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ auto* source =
+ static_cast<std::byte*>(mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
INFO("Could not allocate source memory");
REQUIRE(source != MAP_FAILED);
// Init memory and take snapshots:
init_memory(source, byte_size);
- auto region0 = std::make_unique<simgrid::mc::Region>(page_store_, *memory_.get(), simgrid::mc::RegionType::Data,
- source, byte_size);
+ auto region0 =
+ std::make_unique<simgrid::mc::Region>(page_store_, memory_, simgrid::mc::RegionType::Data, source, byte_size);
for (int i = 0; i < n; i += 2) {
- init_memory((char*)source + i * xbt_pagesize, xbt_pagesize);
+ init_memory(source + i * xbt_pagesize, xbt_pagesize);
}
- auto region = std::make_unique<simgrid::mc::Region>(page_store_, *memory_.get(), simgrid::mc::RegionType::Data,
- source, byte_size);
+ auto region =
+ std::make_unique<simgrid::mc::Region>(page_store_, memory_, simgrid::mc::RegionType::Data, source, byte_size);
- void* destination = mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ auto* destination =
+ static_cast<std::byte*>(mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
INFO("Could not allocate destination memory");
REQUIRE(destination != MAP_FAILED);
}
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(®ion2, ret.src) == some_global_pointer);
{
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();
}
}
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;
}
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)
}
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);
}
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
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
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_;
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_;
}
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;
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
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)) {
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 *************************** */
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)
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();
}
});
#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"
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
/* 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)
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
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);
- :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.
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);
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);
}
#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"
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()
}
});
- 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();
});
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);
}
* 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>
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());
}
});
- 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);
}
* 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"
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());
});
// 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())
+++ /dev/null
-#include <simgrid/Exception.hpp>
-#include <simgrid/plugins/operation.hpp>
-#include <simgrid/s4u/Comm.hpp>
-#include <simgrid/s4u/Exec.hpp>
-#include <simgrid/simix.hpp>
-
-#include "src/simgrid/module.hpp"
-
-SIMGRID_REGISTER_PLUGIN(operation, "Battery management", nullptr)
-/** @defgroup plugin_operation plugin_operation Plugin Operation
-
- @beginrst
-
-This is the operation plugin, enabling management of Operations.
-To activate this plugin, first call :cpp:func:`Operation::init`.
-
-Operations are designed to represent workflows, i.e, graphs of Operations.
-Operations can only be instancied using either
-:cpp:func:`simgrid::plugins::ExecOp::init` or :cpp:func:`simgrid::plugins::CommOp::init`
-An ExecOp is an Execution Operation. Its underlying Activity is an :ref:`Exec <API_s4u_Exec>`.
-A CommOp is a Communication Operation. Its underlying Activity is a :ref:`Comm <API_s4u_Comm>`.
-
- @endrst
- */
-XBT_LOG_NEW_DEFAULT_SUBCATEGORY(Operation, kernel, "Logging specific to the operation plugin");
-
-namespace simgrid::plugins {
-
-xbt::signal<void(Operation*)> Operation::on_start;
-xbt::signal<void(Operation*)> Operation::on_end;
-
-Operation::Operation(const std::string& name) : name_(name) {}
-
-/**
- * @param predecessor The Operation to add.
- * @brief Add a predecessor to this Operation.
- */
-void Operation::add_predecessor(Operation* predecessor)
-{
- if (predecessors_.find(predecessor) == predecessors_.end())
- simgrid::kernel::actor::simcall_answered([this, predecessor] { predecessors_[predecessor] = 0; });
-}
-
-/**
- * @param predecessor The Operation to remove.
- * @brief Remove a predecessor from this Operation.
- */
-void Operation::remove_predecessor(Operation* predecessor)
-{
- simgrid::kernel::actor::simcall_answered([this, predecessor] { predecessors_.erase(predecessor); });
-}
-
-/**
- * @brief Return True if the Operation can start a new Activity.
- * @note The Operation is ready if not already doing something and there is at least one execution waiting in queue.
- */
-bool Operation::ready_to_run() const
-{
- return not working_ && queued_execs_ > 0;
-}
-
-/**
- * @param source The sender.
- * @brief Receive a token from another Operation.
- * @note Check upon reception if the Operation has received a token from each of its predecessors,
- * and in this case consumes those tokens and enqueue an execution.
- */
-void Operation::receive(Operation* source)
-{
- XBT_DEBUG("Operation %s received a token from %s", name_.c_str(), source->name_.c_str());
- auto it = predecessors_.find(source);
- simgrid::kernel::actor::simcall_answered([this, it] {
- it->second++;
- bool enough_tokens = true;
- for (auto const& [key, val] : predecessors_)
- if (val < 1) {
- enough_tokens = false;
- break;
- }
- if (enough_tokens) {
- for (auto& [key, val] : predecessors_)
- val--;
- enqueue_execs(1);
- }
- });
-}
-
-/**
- * @brief Operation routine when finishing an execution.
- * @note Set its working status as false.
- * Add 1 to its count of finished executions.
- * Call the on_this_end func.
- * Fire on_end callback.
- * Send a token to each of its successors.
- * Start a new execution if possible.
- */
-void Operation::complete()
-{
- simgrid::kernel::actor::simcall_answered([this] {
- working_ = false;
- count_++;
- });
- if (end_func_)
- end_func_(this);
- Operation::on_end(this);
- for (auto const& op : successors_)
- op->receive(this);
- if (ready_to_run())
- execute();
-}
-
-/** @ingroup plugin_operation
- * @brief Init the Operation plugin.
- * @note Add a completion callback to all Activities to call Operation::complete().
- */
-void Operation::init()
-{
- if (Operation::inited_)
- return;
- Operation::inited_ = true;
- ExtendedAttributeActivity::EXTENSION_ID = simgrid::s4u::Activity::extension_create<ExtendedAttributeActivity>();
- simgrid::s4u::Activity::on_completion_cb([](simgrid::s4u::Activity const& activity) {
- activity.extension<ExtendedAttributeActivity>()->operation_->complete();
- });
-}
-
-/** @ingroup plugin_operation
- * @param n The number of executions to enqueue.
- * @brief Enqueue executions.
- * @note Immediatly starts an execution if possible.
- */
-void Operation::enqueue_execs(int n)
-{
- simgrid::kernel::actor::simcall_answered([this, n] {
- queued_execs_ += n;
- if (ready_to_run())
- execute();
- });
-}
-
-/** @ingroup plugin_operation
- * @param amount The amount to set.
- * @brief Set the amout of work to do.
- * @note Amount in flop for ExecOp and in bytes for CommOp.
- */
-void Operation::set_amount(double amount)
-{
- simgrid::kernel::actor::simcall_answered([this, amount] { amount_ = amount; });
-}
-
-/** @ingroup plugin_operation
- * @param successor The Operation to add.
- * @brief Add a successor to this Operation.
- * @note It also adds this as a predecessor of successor.
- */
-void Operation::add_successor(OperationPtr successor)
-{
- simgrid::kernel::actor::simcall_answered([this, successor] { successors_.insert(successor.get()); });
- successor->add_predecessor(this);
-}
-
-/** @ingroup plugin_operation
- * @param successor The Operation to remove.
- * @brief Remove a successor from this Operation.
- * @note It also remove this from the predecessors of successor.
- */
-void Operation::remove_successor(OperationPtr successor)
-{
- simgrid::kernel::actor::simcall_answered([this, successor] { successors_.erase(successor.get()); });
- successor->remove_predecessor(this);
-}
-
-void Operation::remove_all_successors()
-{
- simgrid::kernel::actor::simcall_answered([this] {
- while (not successors_.empty()) {
- auto* successor = *(successors_.begin());
- successor->predecessors_.erase(this);
- successors_.erase(successor);
- }
- });
-}
-
-/** @ingroup plugin_operation
- * @param func The function to set.
- * @brief Set a function to be called before each execution.
- * @note The function is called before the underlying Activity starts.
- */
-void Operation::on_this_start(const std::function<void(Operation*)>& func)
-{
- simgrid::kernel::actor::simcall_answered([this, &func] { start_func_ = func; });
-}
-
-/** @ingroup plugin_operation
- * @param func The function to set.
- * @brief Set a function to be called after each execution.
- * @note The function is called after the underlying Activity ends, but before sending tokens to successors.
- */
-void Operation::on_this_end(const std::function<void(Operation*)>& func)
-{
- simgrid::kernel::actor::simcall_answered([this, &func] { end_func_ = func; });
-}
-
-/** @ingroup plugin_operation
- * @brief Return the number of completed executions.
- */
-int Operation::get_count() const
-{
- return count_;
-}
-
-/**
- * @brief Default constructor.
- */
-ExecOp::ExecOp(const std::string& name) : Operation(name) {}
-
-/** @ingroup plugin_operation
- * @brief Smart Constructor.
- */
-ExecOpPtr ExecOp::init(const std::string& name)
-{
- return ExecOpPtr(new ExecOp(name));
-}
-
-/** @ingroup plugin_operation
- * @brief Smart Constructor.
- */
-ExecOpPtr ExecOp::init(const std::string& name, double flops, s4u::Host* host)
-{
- return init(name)->set_flops(flops)->set_host(host);
-}
-
-/**
- * @brief Do one execution of the Operation.
- * @note Call the on_this_start() func. Set its working status as true.
- * Init and start the underlying Activity.
- */
-void ExecOp::execute()
-{
- if (start_func_)
- start_func_(this);
- Operation::on_start(this);
- kernel::actor::simcall_answered([this] {
- working_ = true;
- queued_execs_ = std::max(queued_execs_ - 1, 0);
- });
- s4u::ExecPtr exec = s4u::Exec::init();
- exec->set_name(name_);
- exec->set_flops_amount(amount_);
- exec->set_host(host_);
- exec->start();
- exec->extension_set(new ExtendedAttributeActivity());
- exec->extension<ExtendedAttributeActivity>()->operation_ = this;
- kernel::actor::simcall_answered([this, exec] { current_activity_ = exec; });
-}
-
-/** @ingroup plugin_operation
- * @param host The host to set.
- * @brief Set a new host.
- */
-ExecOpPtr ExecOp::set_host(s4u::Host* host)
-{
- kernel::actor::simcall_answered([this, host] { host_ = host; });
- return this;
-}
-
-/** @ingroup plugin_operation
- * @param flops The amount of flops to set.
- */
-ExecOpPtr ExecOp::set_flops(double flops)
-{
- kernel::actor::simcall_answered([this, flops] { amount_ = flops; });
- return this;
-}
-
-/**
- * @brief Default constructor.
- */
-CommOp::CommOp(const std::string& name) : Operation(name) {}
-
-/** @ingroup plugin_operation
- * @brief Smart constructor.
- */
-CommOpPtr CommOp::init(const std::string& name)
-{
- return CommOpPtr(new CommOp(name));
-}
-
-/** @ingroup plugin_operation
- * @brief Smart constructor.
- */
-CommOpPtr CommOp::init(const std::string& name, double bytes, s4u::Host* source,
- s4u::Host* destination)
-{
- return init(name)->set_bytes(bytes)->set_source(source)->set_destination(destination);
-}
-
-/**
- * @brief Do one execution of the Operation.
- * @note Call the on_this_start() func. Set its working status as true.
- * Init and start the underlying Activity.
- */
-void CommOp::execute()
-{
- if (start_func_)
- start_func_(this);
- Operation::on_start(this);
- kernel::actor::simcall_answered([this] {
- working_ = true;
- queued_execs_ = std::max(queued_execs_ - 1, 0);
- });
- s4u::CommPtr comm = s4u::Comm::sendto_init(source_, destination_);
- comm->set_name(name_);
- comm->set_payload_size(amount_);
- comm->start();
- comm->extension_set(new ExtendedAttributeActivity());
- comm->extension<ExtendedAttributeActivity>()->operation_ = this;
- kernel::actor::simcall_answered([this, comm] { current_activity_ = comm; });
-}
-
-/** @ingroup plugin_operation
- * @param source The host to set.
- * @brief Set a new source host.
- */
-CommOpPtr CommOp::set_source(s4u::Host* source)
-{
- kernel::actor::simcall_answered([this, source] { source_ = source; });
- return this;
-}
-
-/** @ingroup plugin_operation
- * @param destination The host to set.
- * @brief Set a new destination host.
- */
-CommOpPtr CommOp::set_destination(s4u::Host* destination)
-{
- kernel::actor::simcall_answered([this, destination] { destination_ = destination; });
- return this;
-}
-
-/** @ingroup plugin_operation
- * @param bytes The amount of bytes to set.
- */
-CommOpPtr CommOp::set_bytes(double bytes)
-{
- kernel::actor::simcall_answered([this, bytes] { amount_ = bytes; });
- return this;
-}
-
-} // namespace simgrid::plugins
-
-simgrid::xbt::Extension<simgrid::s4u::Activity, simgrid::plugins::ExtendedAttributeActivity>
- simgrid::plugins::ExtendedAttributeActivity::EXTENSION_ID;
-bool simgrid::plugins::Operation::inited_ = false;
--- /dev/null
+#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
--- /dev/null
+#include <simgrid/Exception.hpp>
+#include <simgrid/plugins/task.hpp>
+#include <simgrid/s4u/Comm.hpp>
+#include <simgrid/s4u/Exec.hpp>
+#include <simgrid/s4u/Io.hpp>
+#include <simgrid/simix.hpp>
+
+#include "src/simgrid/module.hpp"
+
+SIMGRID_REGISTER_PLUGIN(task, "Battery management", nullptr)
+/** @defgroup plugin_task plugin_task Plugin Task
+
+ @beginrst
+
+This is the task plugin, enabling management of Tasks.
+To activate this plugin, first call :cpp:func:`Task::init`.
+
+Tasks are designed to represent dataflows, i.e, graphs of Tasks.
+Tasks can only be instancied using either
+:cpp:func:`simgrid::plugins::ExecTask::init` or :cpp:func:`simgrid::plugins::CommTask::init`
+An ExecTask is an Execution Task. Its underlying Activity is an :ref:`Exec <API_s4u_Exec>`.
+A CommTask is a Communication Task. Its underlying Activity is a :ref:`Comm <API_s4u_Comm>`.
+
+ @endrst
+ */
+XBT_LOG_NEW_DEFAULT_SUBCATEGORY(Task, kernel, "Logging specific to the task plugin");
+
+namespace simgrid::plugins {
+
+xbt::signal<void(Task*)> Task::on_start;
+xbt::signal<void(Task*)> Task::on_end;
+
+Task::Task(const std::string& name) : name_(name) {}
+
+/**
+ * @param predecessor The Task to add.
+ * @brief Add a predecessor to this Task.
+ */
+void Task::add_predecessor(Task* predecessor)
+{
+ if (predecessors_.find(predecessor) == predecessors_.end())
+ simgrid::kernel::actor::simcall_answered([this, predecessor] { predecessors_[predecessor] = 0; });
+}
+
+/**
+ * @param predecessor The Task to remove.
+ * @brief Remove a predecessor from this Task.
+ */
+void Task::remove_predecessor(Task* predecessor)
+{
+ simgrid::kernel::actor::simcall_answered([this, predecessor] { predecessors_.erase(predecessor); });
+}
+
+/**
+ * @brief Return True if the Task can start a new Activity.
+ * @note The Task is ready if not already doing something and there is at least one execution waiting in queue.
+ */
+bool Task::ready_to_run() const
+{
+ return not working_ && queued_execs_ > 0;
+}
+
+/**
+ * @param source The sender.
+ * @brief Receive a token from another Task.
+ * @note Check upon reception if the Task has received a token from each of its predecessors,
+ * and in this case consumes those tokens and enqueue an execution.
+ */
+void Task::receive(Task* source)
+{
+ XBT_DEBUG("Task %s received a token from %s", name_.c_str(), source->name_.c_str());
+ auto it = predecessors_.find(source);
+ simgrid::kernel::actor::simcall_answered([this, it] {
+ it->second++;
+ bool enough_tokens = true;
+ for (auto const& [key, val] : predecessors_)
+ if (val < 1) {
+ enough_tokens = false;
+ break;
+ }
+ if (enough_tokens) {
+ for (auto& [key, val] : predecessors_)
+ val--;
+ enqueue_execs(1);
+ }
+ });
+}
+
+/**
+ * @brief Task routine when finishing an execution.
+ * @note Set its working status as false.
+ * Add 1 to its count of finished executions.
+ * Call the on_this_end func.
+ * Fire on_end callback.
+ * Send a token to each of its successors.
+ * Start a new execution if possible.
+ */
+void Task::complete()
+{
+ simgrid::kernel::actor::simcall_answered([this] {
+ working_ = false;
+ count_++;
+ });
+ for (auto end_func : end_func_handlers_)
+ end_func(this);
+ Task::on_end(this);
+ for (auto const& t : successors_)
+ t->receive(this);
+ if (ready_to_run())
+ fire();
+}
+
+/** @ingroup plugin_task
+ * @brief Init the Task plugin.
+ * @note Add a completion callback to all Activities to call Task::complete().
+ */
+void Task::init()
+{
+ if (Task::inited_)
+ return;
+ Task::inited_ = true;
+ ExtendedAttributeActivity::EXTENSION_ID = simgrid::s4u::Activity::extension_create<ExtendedAttributeActivity>();
+ simgrid::s4u::Exec::on_completion_cb(
+ [](simgrid::s4u::Exec const& exec) { exec.extension<ExtendedAttributeActivity>()->task_->complete(); });
+ simgrid::s4u::Comm::on_completion_cb(
+ [](simgrid::s4u::Comm const& comm) { comm.extension<ExtendedAttributeActivity>()->task_->complete(); });
+ simgrid::s4u::Io::on_completion_cb(
+ [](simgrid::s4u::Io const& io) { io.extension<ExtendedAttributeActivity>()->task_->complete(); });
+}
+
+/** @ingroup plugin_task
+ * @param n The number of executions to enqueue.
+ * @brief Enqueue executions.
+ * @note Immediatly starts an execution if possible.
+ */
+void Task::enqueue_execs(int n)
+{
+ simgrid::kernel::actor::simcall_answered([this, n] {
+ queued_execs_ += n;
+ if (ready_to_run())
+ fire();
+ });
+}
+
+/** @ingroup plugin_task
+ * @param amount The amount to set.
+ * @brief Set the amout of work to do.
+ * @note Amount in flop for ExecTask and in bytes for CommTask.
+ */
+void Task::set_amount(double amount)
+{
+ simgrid::kernel::actor::simcall_answered([this, amount] { amount_ = amount; });
+}
+
+/** @ingroup plugin_task
+ * @param successor The Task to add.
+ * @brief Add a successor to this Task.
+ * @note It also adds this as a predecessor of successor.
+ */
+void Task::add_successor(TaskPtr successor)
+{
+ simgrid::kernel::actor::simcall_answered([this, successor] { successors_.insert(successor.get()); });
+ successor->add_predecessor(this);
+}
+
+/** @ingroup plugin_task
+ * @param successor The Task to remove.
+ * @brief Remove a successor from this Task.
+ * @note It also remove this from the predecessors of successor.
+ */
+void Task::remove_successor(TaskPtr successor)
+{
+ simgrid::kernel::actor::simcall_answered([this, successor] { successors_.erase(successor.get()); });
+ successor->remove_predecessor(this);
+}
+
+void Task::remove_all_successors()
+{
+ simgrid::kernel::actor::simcall_answered([this] {
+ while (not successors_.empty()) {
+ auto* successor = *(successors_.begin());
+ successor->predecessors_.erase(this);
+ successors_.erase(successor);
+ }
+ });
+}
+
+/** @ingroup plugin_task
+ * @param func The function to set.
+ * @brief Set a function to be called before each execution.
+ * @note The function is called before the underlying Activity starts.
+ */
+void Task::on_this_start(const std::function<void(Task*)>& func)
+{
+ simgrid::kernel::actor::simcall_answered([this, &func] { start_func_handlers_.push_back(func); });
+}
+
+/** @ingroup plugin_task
+ * @param func The function to set.
+ * @brief Set a function to be called after each execution.
+ * @note The function is called after the underlying Activity ends, but before sending tokens to successors.
+ */
+void Task::on_this_end(const std::function<void(Task*)>& func)
+{
+ simgrid::kernel::actor::simcall_answered([this, &func] { end_func_handlers_.push_back(func); });
+}
+
+/** @ingroup plugin_task
+ * @brief Return the number of completed executions.
+ */
+int Task::get_count() const
+{
+ return count_;
+}
+
+/**
+ * @brief Default constructor.
+ */
+ExecTask::ExecTask(const std::string& name) : Task(name) {}
+
+/** @ingroup plugin_task
+ * @brief Smart Constructor.
+ */
+ExecTaskPtr ExecTask::init(const std::string& name)
+{
+ return ExecTaskPtr(new ExecTask(name));
+}
+
+/** @ingroup plugin_task
+ * @brief Smart Constructor.
+ */
+ExecTaskPtr ExecTask::init(const std::string& name, double flops, s4u::Host* host)
+{
+ return init(name)->set_flops(flops)->set_host(host);
+}
+
+/**
+ * @brief Do one execution of the Task.
+ * @note Call the on_this_start() func. Set its working status as true.
+ * Init and start the underlying Activity.
+ */
+void ExecTask::fire()
+{
+ for (auto start_func : start_func_handlers_)
+ start_func(this);
+ Task::on_start(this);
+ kernel::actor::simcall_answered([this] {
+ working_ = true;
+ queued_execs_ = std::max(queued_execs_ - 1, 0);
+ });
+ s4u::ExecPtr exec = s4u::Exec::init();
+ exec->set_name(name_);
+ exec->set_flops_amount(amount_);
+ exec->set_host(host_);
+ exec->start();
+ exec->extension_set(new ExtendedAttributeActivity());
+ exec->extension<ExtendedAttributeActivity>()->task_ = this;
+ kernel::actor::simcall_answered([this, exec] { current_activity_ = exec; });
+}
+
+/** @ingroup plugin_task
+ * @param host The host to set.
+ * @brief Set a new host.
+ */
+ExecTaskPtr ExecTask::set_host(s4u::Host* host)
+{
+ kernel::actor::simcall_answered([this, host] { host_ = host; });
+ return this;
+}
+
+/** @ingroup plugin_task
+ * @param flops The amount of flops to set.
+ */
+ExecTaskPtr ExecTask::set_flops(double flops)
+{
+ kernel::actor::simcall_answered([this, flops] { amount_ = flops; });
+ return this;
+}
+
+/**
+ * @brief Default constructor.
+ */
+CommTask::CommTask(const std::string& name) : Task(name) {}
+
+/** @ingroup plugin_task
+ * @brief Smart constructor.
+ */
+CommTaskPtr CommTask::init(const std::string& name)
+{
+ return CommTaskPtr(new CommTask(name));
+}
+
+/** @ingroup plugin_task
+ * @brief Smart constructor.
+ */
+CommTaskPtr CommTask::init(const std::string& name, double bytes, s4u::Host* source, s4u::Host* destination)
+{
+ return init(name)->set_bytes(bytes)->set_source(source)->set_destination(destination);
+}
+
+/**
+ * @brief Do one execution of the Task.
+ * @note Call the on_this_start() func. Set its working status as true.
+ * Init and start the underlying Activity.
+ */
+void CommTask::fire()
+{
+ for (auto start_func : start_func_handlers_)
+ start_func(this);
+ Task::on_start(this);
+ kernel::actor::simcall_answered([this] {
+ working_ = true;
+ queued_execs_ = std::max(queued_execs_ - 1, 0);
+ });
+ s4u::CommPtr comm = s4u::Comm::sendto_init(source_, destination_);
+ comm->set_name(name_);
+ comm->set_payload_size(amount_);
+ comm->start();
+ comm->extension_set(new ExtendedAttributeActivity());
+ comm->extension<ExtendedAttributeActivity>()->task_ = this;
+ kernel::actor::simcall_answered([this, comm] { current_activity_ = comm; });
+}
+
+/** @ingroup plugin_task
+ * @param source The host to set.
+ * @brief Set a new source host.
+ */
+CommTaskPtr CommTask::set_source(s4u::Host* source)
+{
+ kernel::actor::simcall_answered([this, source] { source_ = source; });
+ return this;
+}
+
+/** @ingroup plugin_task
+ * @param destination The host to set.
+ * @brief Set a new destination host.
+ */
+CommTaskPtr CommTask::set_destination(s4u::Host* destination)
+{
+ kernel::actor::simcall_answered([this, destination] { destination_ = destination; });
+ return this;
+}
+
+/** @ingroup plugin_task
+ * @param bytes The amount of bytes to set.
+ */
+CommTaskPtr CommTask::set_bytes(double bytes)
+{
+ kernel::actor::simcall_answered([this, bytes] { amount_ = bytes; });
+ return this;
+}
+
+/**
+ * @brief Default constructor.
+ */
+IoTask::IoTask(const std::string& name) : Task(name) {}
+
+/** @ingroup plugin_task
+ * @brief Smart Constructor.
+ */
+IoTaskPtr IoTask::init(const std::string& name)
+{
+ return IoTaskPtr(new IoTask(name));
+}
+
+/** @ingroup plugin_task
+ * @brief Smart Constructor.
+ */
+IoTaskPtr IoTask::init(const std::string& name, double bytes, s4u::Disk* disk, s4u::Io::OpType type)
+{
+ return init(name)->set_bytes(bytes)->set_disk(disk)->set_op_type(type);
+}
+
+/** @ingroup plugin_task
+ * @param disk The disk to set.
+ * @brief Set a new disk.
+ */
+IoTaskPtr IoTask::set_disk(s4u::Disk* disk)
+{
+ kernel::actor::simcall_answered([this, disk] { disk_ = disk; });
+ return this;
+}
+
+/** @ingroup plugin_task
+ * @param bytes The amount of bytes to set.
+ */
+IoTaskPtr IoTask::set_bytes(double bytes)
+{
+ kernel::actor::simcall_answered([this, bytes] { amount_ = bytes; });
+ return this;
+}
+
+/** @ingroup plugin_task */
+IoTaskPtr IoTask::set_op_type(s4u::Io::OpType type)
+{
+ kernel::actor::simcall_answered([this, type] { type_ = type; });
+ return this;
+}
+
+void IoTask::fire()
+{
+ for (auto start_func : start_func_handlers_)
+ start_func(this);
+ Task::on_start(this);
+ kernel::actor::simcall_answered([this] {
+ working_ = true;
+ queued_execs_ = std::max(queued_execs_ - 1, 0);
+ });
+ s4u::IoPtr io = s4u::Io::init();
+ io->set_name(name_);
+ io->set_size(amount_);
+ io->set_disk(disk_);
+ io->set_op_type(type_);
+ io->start();
+ io->extension_set(new ExtendedAttributeActivity());
+ io->extension<ExtendedAttributeActivity>()->task_ = this;
+ kernel::actor::simcall_answered([this, io] { current_activity_ = io; });
+}
+
+} // namespace simgrid::plugins
+
+simgrid::xbt::Extension<simgrid::s4u::Activity, simgrid::plugins::ExtendedAttributeActivity>
+ simgrid::plugins::ExtendedAttributeActivity::EXTENSION_ID;
+bool simgrid::plugins::Task::inited_ = false;
}
}
-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());
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);
}
}
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()
});
s4u::Actor::on_host_change(*this, *previous_location);
+ s4u::Actor::on_this_host_change(*this, *previous_location);
}
s4u::Host* Actor::get_host() const
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) {
{
kernel::actor::simcall_answered([this] { pimpl_->resume(); });
s4u::Actor::on_resume(*this);
+ s4u::Actor::on_this_resume(*this);
}
bool Actor::is_suspended() const
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()) {
});
Actor::on_wake_up(*issuer->get_ciface());
+ issuer->get_ciface()->on_this_wake_up(*issuer->get_ciface());
}
void yield()
{
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(); });
}
XBT_INFO("pimpl_ is null");
xbt_backtrace_display_current();
}
+ if (pimpl_ != nullptr)
+ pimpl_->set_iface(nullptr);
}
void Comm::send(kernel::actor::ActorImpl* sender, const Mailbox* mbox, double task_size, double rate, void* src_buff,
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()) ||
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_,
} 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_),
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;
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_);
}
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
{
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)
{
state_ = State::STARTED;
on_start(*this);
+ on_this_start(*this);
return this;
}
#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)
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);
});
}
}
this->pimpl_cpu_->turn_off();
this->pimpl_->turn_off(self);
- on_state_change(*this);
+ on_onoff(*this);
+ on_this_onoff(*this);
});
}
}
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)
{
state_ = State::STARTED;
on_start(*this);
+ on_this_start(*this);
return this;
}
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;
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()); });
#ifndef SMPI_TOPO_HPP_INCLUDED
#define SMPI_TOPO_HPP_INCLUDED
-#include "smpi_comm.hpp"
#include "smpi_status.hpp"
#include <memory>
}
// 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)
{
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;
{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();
}
});
- 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);
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 */
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");
{
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);
{
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())
> If this is too much, consider sharing allocations for computation buffers.
> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
>
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 635 unique states visited; 173 backtracks (3896 transition replays, 3088 states visited overall)
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed MPI handles:
+> [0.000000] [smpi_utils/WARNING] To get more information (location of allocations), compile your code with -trace-call-location flag of smpicc/f90
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Comm
+> [0.000000] [smpi_utils/INFO] 4 leaked handles of type MPI_Group
+> [0.000000] [smpi_utils/INFO] Probable memory leaks in your code: SMPI detected 8 unfreed buffers:
+> [0.000000] [smpi_utils/INFO] leaked allocations of total size 152, called 8 times, with minimum size 16 and maximum size 28
+> [0.000000] [smpi_utils/INFO] Memory Usage: Simulated application allocated 152 bytes during its lifetime through malloc/calloc calls.
+> Largest allocation at once from a single process was 28 bytes, at coll-allreduce-with-leaks.c:28. It was called 1 times during the whole simulation.
+> If this is too much, consider sharing allocations for computation buffers.
+> This can be done automatically by setting --cfg=smpi/auto-shared-malloc-thresh to the minimum size wanted size (this can alter execution if data content is necessary)
+>
+> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1005 unique states visited; 276 backtracks (6560 transition replays, 5279 states visited overall)
{
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();
}
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$
src/plugins/vm/VmLiveMigration.hpp
src/plugins/vm/dirty_page_tracking.cpp
src/plugins/battery.cpp
- src/plugins/operation.cpp
+ src/plugins/task.cpp
+ src/plugins/photovoltaic.cpp
)
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/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
include/simgrid/plugins/file_system.h
include/simgrid/plugins/live_migration.h
include/simgrid/plugins/load.h
- include/simgrid/plugins/operation.hpp
+ include/simgrid/plugins/task.hpp
+ include/simgrid/plugins/photovoltaic.hpp
include/simgrid/plugins/ProducerConsumer.hpp
include/simgrid/instr.h
include/simgrid/mailbox.h
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
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