From 7672ae43c2b49a7dcdc0976cc89a05cc87ae534c Mon Sep 17 00:00:00 2001 From: Martin Quinson Date: Sun, 6 Nov 2022 01:17:57 +0100 Subject: [PATCH] setter function only need a simcall in MC or with parallel execs This change introduce a new kind of simcalls alongside simcall_answered and simcall_blocking: simcall_run_object_access that is dedicated to object setter functions. This is an answered simcall if running in // or in MC, and just a regular function call if runnning a plain simulation. This may speed up large simulations with a huge amount of activities, but the perf improvement is not evaluated yet. The performance could be further improved with a SIMGRID_HAVE_PARALLEL option allowing the compiler to know beforhand that the simcall branch will never be taken. But such an option would require to be tested and I'm too lazy for that. --- examples/c/exec-dvfs/exec-dvfs.tesh | 4 +-- examples/cpp/exec-dvfs/s4u-exec-dvfs.tesh | 4 +-- examples/python/exec-dvfs/exec-dvfs.tesh | 4 +-- include/simgrid/forward.h | 2 ++ include/simgrid/simix.hpp | 35 ++++++++++++++---- src/kernel/activity/ActivityImpl.hpp | 16 ++++----- src/kernel/actor/ActorImpl.hpp | 3 ++ src/kernel/actor/CommObserver.cpp | 7 ---- src/kernel/actor/Simcall.cpp | 10 +++++- src/kernel/actor/Simcall.hpp | 17 +++++---- src/kernel/actor/SimcallObserver.cpp | 20 +++++++++++ src/kernel/actor/SimcallObserver.hpp | 26 ++++++++++++++ src/kernel/resource/Resource.hpp | 3 +- src/mc/compare.cpp | 1 + src/mc/transition/Transition.cpp | 1 + src/mc/transition/Transition.hpp | 3 +- src/s4u/s4u_ConditionVariable.cpp | 1 + src/s4u/s4u_Disk.cpp | 22 ++++++------ src/s4u/s4u_Exec.cpp | 25 +++++++------ src/s4u/s4u_Host.cpp | 22 ++++++------ src/s4u/s4u_Io.cpp | 36 +++++++++---------- src/s4u/s4u_Link.cpp | 18 +++++----- src/s4u/s4u_VirtualMachine.cpp | 1 + src/simix/libsmx.cpp | 28 +++++++++++++++ src/surf/HostImpl.hpp | 2 +- .../storage_client_server.tesh | 4 +-- 26 files changed, 216 insertions(+), 99 deletions(-) diff --git a/examples/c/exec-dvfs/exec-dvfs.tesh b/examples/c/exec-dvfs/exec-dvfs.tesh index 882c433589..5485497ca6 100644 --- a/examples/c/exec-dvfs/exec-dvfs.tesh +++ b/examples/c/exec-dvfs/exec-dvfs.tesh @@ -7,9 +7,9 @@ $ ${bindir:=.}/c-exec-dvfs ${platfdir}/energy_platform.xml "--log=root.fmt:[%10. > [ 0.000000] (2:dvfs_test@MyHost2) Current power peak=100000000.000000 > [ 1.000000] (1:dvfs_test@MyHost1) Task1 simulation time: 1.000000e+00 > [ 1.000000] (1:dvfs_test@MyHost1) Changing power peak value to 20000000.000000 (at index 2) +> [ 1.000000] (1:dvfs_test@MyHost1) Current power peak=20000000.000000 > [ 1.000000] (2:dvfs_test@MyHost2) Task1 simulation time: 1.000000e+00 > [ 1.000000] (2:dvfs_test@MyHost2) Changing power peak value to 20000000.000000 (at index 2) -> [ 1.000000] (1:dvfs_test@MyHost1) Current power peak=20000000.000000 > [ 1.000000] (2:dvfs_test@MyHost2) Current power peak=20000000.000000 > [ 6.000000] (1:dvfs_test@MyHost1) Task2 simulation time: 5.000000e+00 > [ 6.000000] (1:dvfs_test@MyHost1) Count of Processor states=3 @@ -28,9 +28,9 @@ $ ${bindir:=.}/c-exec-dvfs ${platfdir}/energy_cluster.xml "--log=root.fmt:[%10.6 > [ 0.000000] (2:dvfs_test@MyHost2) Current power peak=100000000.000000 > [ 1.000000] (1:dvfs_test@MyHost1) Task1 simulation time: 1.000000e+00 > [ 1.000000] (1:dvfs_test@MyHost1) Changing power peak value to 20000000.000000 (at index 2) +> [ 1.000000] (1:dvfs_test@MyHost1) Current power peak=20000000.000000 > [ 1.000000] (2:dvfs_test@MyHost2) Task1 simulation time: 1.000000e+00 > [ 1.000000] (2:dvfs_test@MyHost2) Changing power peak value to 20000000.000000 (at index 2) -> [ 1.000000] (1:dvfs_test@MyHost1) Current power peak=20000000.000000 > [ 1.000000] (2:dvfs_test@MyHost2) Current power peak=20000000.000000 > [ 6.000000] (1:dvfs_test@MyHost1) Task2 simulation time: 5.000000e+00 > [ 6.000000] (1:dvfs_test@MyHost1) Count of Processor states=3 diff --git a/examples/cpp/exec-dvfs/s4u-exec-dvfs.tesh b/examples/cpp/exec-dvfs/s4u-exec-dvfs.tesh index 8ad80a9493..f0febb1893 100644 --- a/examples/cpp/exec-dvfs/s4u-exec-dvfs.tesh +++ b/examples/cpp/exec-dvfs/s4u-exec-dvfs.tesh @@ -9,9 +9,9 @@ $ ${bindir:=.}/s4u-exec-dvfs ${platfdir}/energy_platform.xml "--log=root.fmt:[%1 > [ 0.000000] (2:dvfs_test@MyHost2) Current power peak=100000000.000000 > [ 1.000000] (1:dvfs_test@MyHost1) Computation1 duration: 1.00 > [ 1.000000] (1:dvfs_test@MyHost1) Changing power peak value to 20000000.000000 (at index 2) +> [ 1.000000] (1:dvfs_test@MyHost1) Current power peak=20000000.000000 > [ 1.000000] (2:dvfs_test@MyHost2) Computation1 duration: 1.00 > [ 1.000000] (2:dvfs_test@MyHost2) Changing power peak value to 20000000.000000 (at index 2) -> [ 1.000000] (1:dvfs_test@MyHost1) Current power peak=20000000.000000 > [ 1.000000] (2:dvfs_test@MyHost2) Current power peak=20000000.000000 > [ 6.000000] (1:dvfs_test@MyHost1) Computation2 duration: 5.00 > [ 6.000000] (1:dvfs_test@MyHost1) Count of Processor states=3 @@ -28,9 +28,9 @@ $ ${bindir:=.}/s4u-exec-dvfs ${platfdir}/energy_cluster.xml "--log=root.fmt:[%10 > [ 0.000000] (2:dvfs_test@MyHost2) Current power peak=100000000.000000 > [ 1.000000] (1:dvfs_test@MyHost1) Computation1 duration: 1.00 > [ 1.000000] (1:dvfs_test@MyHost1) Changing power peak value to 20000000.000000 (at index 2) +> [ 1.000000] (1:dvfs_test@MyHost1) Current power peak=20000000.000000 > [ 1.000000] (2:dvfs_test@MyHost2) Computation1 duration: 1.00 > [ 1.000000] (2:dvfs_test@MyHost2) Changing power peak value to 20000000.000000 (at index 2) -> [ 1.000000] (1:dvfs_test@MyHost1) Current power peak=20000000.000000 > [ 1.000000] (2:dvfs_test@MyHost2) Current power peak=20000000.000000 > [ 6.000000] (1:dvfs_test@MyHost1) Computation2 duration: 5.00 > [ 6.000000] (1:dvfs_test@MyHost1) Count of Processor states=3 diff --git a/examples/python/exec-dvfs/exec-dvfs.tesh b/examples/python/exec-dvfs/exec-dvfs.tesh index 06bd1904af..7055274dbf 100644 --- a/examples/python/exec-dvfs/exec-dvfs.tesh +++ b/examples/python/exec-dvfs/exec-dvfs.tesh @@ -9,9 +9,9 @@ $ ${pythoncmd:=python3} ${PYTHON_TOOL_OPTIONS:=} ${srcdir:=.}/exec-dvfs.py ${pla > [ 0.000000] (2:dvfs_test@MyHost2) Current power peak=100000000.000000 > [ 1.000000] (1:dvfs_test@MyHost1) Task1 duration: 1.00 > [ 1.000000] (1:dvfs_test@MyHost1) Changing power peak value to 20000000.000000 (at index 2) +> [ 1.000000] (1:dvfs_test@MyHost1) Changed power peak=20000000.000000 > [ 1.000000] (2:dvfs_test@MyHost2) Task1 duration: 1.00 > [ 1.000000] (2:dvfs_test@MyHost2) Changing power peak value to 20000000.000000 (at index 2) -> [ 1.000000] (1:dvfs_test@MyHost1) Changed power peak=20000000.000000 > [ 1.000000] (2:dvfs_test@MyHost2) Changed power peak=20000000.000000 > [ 6.000000] (1:dvfs_test@MyHost1) Task2 duration: 5.00 > [ 6.000000] (1:dvfs_test@MyHost1) Count of Processor states=3 @@ -27,9 +27,9 @@ $ ${pythoncmd:=python3} ${PYTHON_TOOL_OPTIONS:=} ${srcdir:=.}/exec-dvfs.py ${pla > [ 0.000000] (2:dvfs_test@MyHost2) Current power peak=100000000.000000 > [ 1.000000] (1:dvfs_test@MyHost1) Task1 duration: 1.00 > [ 1.000000] (1:dvfs_test@MyHost1) Changing power peak value to 20000000.000000 (at index 2) +> [ 1.000000] (1:dvfs_test@MyHost1) Changed power peak=20000000.000000 > [ 1.000000] (2:dvfs_test@MyHost2) Task1 duration: 1.00 > [ 1.000000] (2:dvfs_test@MyHost2) Changing power peak value to 20000000.000000 (at index 2) -> [ 1.000000] (1:dvfs_test@MyHost1) Changed power peak=20000000.000000 > [ 1.000000] (2:dvfs_test@MyHost2) Changed power peak=20000000.000000 > [ 6.000000] (1:dvfs_test@MyHost1) Task2 duration: 5.00 > [ 6.000000] (1:dvfs_test@MyHost1) Count of Processor states=3 diff --git a/include/simgrid/forward.h b/include/simgrid/forward.h index 7cc15659ab..99c62651b9 100644 --- a/include/simgrid/forward.h +++ b/include/simgrid/forward.h @@ -117,6 +117,8 @@ using ActorCodeFactory = std::function args)> class Simcall; class SimcallObserver; +class ObjectAccessSimcallObserver; +class ObjectAccessSimcallItem; } // namespace actor namespace activity { diff --git a/include/simgrid/simix.hpp b/include/simgrid/simix.hpp index fb8addfb3c..ac24d4f820 100644 --- a/include/simgrid/simix.hpp +++ b/include/simgrid/simix.hpp @@ -18,15 +18,15 @@ XBT_PUBLIC void simcall_run_answered(std::function const& code, simgrid::kernel::actor::SimcallObserver* observer); XBT_PUBLIC void simcall_run_blocking(std::function const& code, simgrid::kernel::actor::SimcallObserver* observer); +XBT_PUBLIC void simcall_run_object_access(std::function 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. * * Every modification of the environment must be protected this way: every setter, constructor and similar. - * Getters don't have to be protected this way. + * Getters don't have to be protected this way, and setters may use the simcall_object_access() variant (see below). * * This allows deterministic parallel simulation without any locking, even if almost nobody uses parallel simulation in * SimGrid. More interestingly it makes every modification of the simulated world observable by the model-checker, @@ -58,6 +58,29 @@ template typename std::result_of_t simcall_answered(F&& code, Sim return result.get(); } +/** Use a setter on the `item` object. That's a simcall only if running in parallel or with MC activated. + * + * Simulation without MC and without parallelism (contexts/nthreads=1) will not pay the price of a simcall for an + * harmless setter. When running in parallel, you want your write access to be done in a mutual exclusion way, while the + * getters can still occure out of order. + * + * When running in MC, you want to make this access visible to the checker. Actually in this case, it's not visible from + * the checker (and thus still use a fast track) if the setter is called from the actor that created the object. + */ +template typename std::result_of_t simcall_object_access(ObjectAccessSimcallItem* item, F&& code) +{ + // If we are in the maestro, we take the fast path and execute the code directly + if (simgrid::s4u::Actor::is_maestro()) + return std::forward(code)(); + + // If called from another thread, do a real simcall. It will be short-cut on need + using R = typename std::result_of_t; + simgrid::xbt::Result result; + simcall_run_object_access([&result, &code] { simgrid::xbt::fulfill_promise(result, std::forward(code)); }, item); + + return result.get(); +} + /** Execute some code (that does not return immediately) in kernel context * * This is very similar to simcall() right above, but the calling actor will not get rescheduled until @@ -91,8 +114,6 @@ auto simcall_blocking(F&& code, Observer* observer) -> decltype(observer->get_re simcall_blocking(std::forward(code), static_cast(observer)); return observer->get_result(); } -} // namespace actor -} // namespace kernel -} // namespace simgrid +} // namespace simgrid::kernel::actor #endif diff --git a/src/kernel/activity/ActivityImpl.hpp b/src/kernel/activity/ActivityImpl.hpp index f3dca0b2cc..a4993f90b5 100644 --- a/src/kernel/activity/ActivityImpl.hpp +++ b/src/kernel/activity/ActivityImpl.hpp @@ -6,23 +6,23 @@ #ifndef SIMGRID_KERNEL_ACTIVITY_ACTIVITYIMPL_HPP #define SIMGRID_KERNEL_ACTIVITY_ACTIVITYIMPL_HPP -#include -#include -#include - #include "simgrid/forward.h" -#include +#include "simgrid/kernel/resource/Action.hpp" +#include "simgrid/simix.hpp" +#include "src/kernel/actor/Simcall.hpp" +#include "xbt/utility.hpp" #include -#include -#include +#include +#include +#include namespace simgrid::kernel::activity { XBT_DECLARE_ENUM_CLASS(State, WAITING, READY, RUNNING, DONE, CANCELED, FAILED, SRC_HOST_FAILURE, DST_HOST_FAILURE, TIMEOUT, SRC_TIMEOUT, DST_TIMEOUT, LINK_FAILURE); -class XBT_PUBLIC ActivityImpl { +class XBT_PUBLIC ActivityImpl : public kernel::actor::ObjectAccessSimcallItem { std::atomic_int_fast32_t refcount_{0}; std::string name_ = ""; actor::ActorImpl* actor_ = nullptr; diff --git a/src/kernel/actor/ActorImpl.hpp b/src/kernel/actor/ActorImpl.hpp index 34b61deac8..044bbf5cd0 100644 --- a/src/kernel/actor/ActorImpl.hpp +++ b/src/kernel/actor/ActorImpl.hpp @@ -9,7 +9,10 @@ #include "Simcall.hpp" #include "simgrid/kernel/Timer.hpp" #include "simgrid/s4u/Actor.hpp" +#include "src/kernel/actor/Simcall.hpp" #include "xbt/PropertyHolder.hpp" + +#include #include #include #include diff --git a/src/kernel/actor/CommObserver.cpp b/src/kernel/actor/CommObserver.cpp index 9c556193a3..ae1d392b07 100644 --- a/src/kernel/actor/CommObserver.cpp +++ b/src/kernel/actor/CommObserver.cpp @@ -51,13 +51,6 @@ static void serialize_activity_test(const activity::ActivityImpl* act, std::stri stream << (short)mc::Transition::Type::UNKNOWN; } } -template static std::string ptr_to_id(A* ptr) -{ - static std::unordered_map map; - if (map.find(ptr) == map.end()) - map.insert(std::make_pair(ptr, std::to_string(map.size() + 1))); - return map[ptr]; -} static std::string to_string_activity_test(const activity::ActivityImpl* act) { if (auto* comm = dynamic_cast(act)) { diff --git a/src/kernel/actor/Simcall.cpp b/src/kernel/actor/Simcall.cpp index 09c7cdf614..622a6c8123 100644 --- a/src/kernel/actor/Simcall.cpp +++ b/src/kernel/actor/Simcall.cpp @@ -3,7 +3,7 @@ /* This program is free software; you can redistribute it and/or modify it * under the terms of the license (GNU LGPL) which comes with this package. */ -#include "Simcall.hpp" +#include "src/kernel/actor/Simcall.hpp" #include "simgrid/s4u/Host.hpp" #include "src/kernel/actor/ActorImpl.hpp" #include "src/kernel/actor/SimcallObserver.hpp" @@ -26,5 +26,13 @@ const char* Simcall::get_cname() const return to_c_str(call_); } } +ObjectAccessSimcallItem::ObjectAccessSimcallItem() +{ + take_ownership(); +} +void ObjectAccessSimcallItem::take_ownership() +{ + simcall_owner_ = ActorImpl::self(); +} } // namespace simgrid::kernel::actor diff --git a/src/kernel/actor/Simcall.hpp b/src/kernel/actor/Simcall.hpp index 8e7869fdd5..091040c27c 100644 --- a/src/kernel/actor/Simcall.hpp +++ b/src/kernel/actor/Simcall.hpp @@ -3,18 +3,15 @@ /* This program is free software; you can redistribute it and/or modify it * under the terms of the license (GNU LGPL) which comes with this package. */ -#ifndef SIMCALL_HPP -#define SIMCALL_HPP +#ifndef SIMGRID_SIMCALL_HPP +#define SIMGRID_SIMCALL_HPP #include "simgrid/forward.h" -#include "src/kernel/activity/ActivityImpl.hpp" #include "xbt/utility.hpp" namespace simgrid::kernel::actor { -/** - * @brief Represents a simcall to the kernel. - */ +/** Contains what's needed to run some code in kernel mode on behalf of an actor */ class Simcall { public: /** All possible simcalls. */ @@ -29,6 +26,14 @@ public: const char* get_cname() const; }; +/** A thing that can be used for an ObjectAccess simcall (getter or setter). */ +class ObjectAccessSimcallItem { +public: + ObjectAccessSimcallItem(); + void take_ownership(); + ActorImpl* simcall_owner_; +}; + } // namespace simgrid::kernel::actor #endif diff --git a/src/kernel/actor/SimcallObserver.cpp b/src/kernel/actor/SimcallObserver.cpp index c07d073e27..0246d5a324 100644 --- a/src/kernel/actor/SimcallObserver.cpp +++ b/src/kernel/actor/SimcallObserver.cpp @@ -71,4 +71,24 @@ std::string ActorJoinSimcall::to_string() const { return std::string("ActorJoin(pid:") + std::to_string(other_->get_pid()) + ")"; } + +void ObjectAccessSimcallObserver::serialize(std::stringstream& stream) const +{ + stream << (short)mc::Transition::Type::OBJECT_ACCESS << ' '; + stream << object_ << ' ' << get_owner()->get_pid(); +} +std::string ObjectAccessSimcallObserver::to_string() const +{ + return std::string("ObjectAccess(obj:") + ptr_to_id(object_) + + " owner:" + std::to_string(get_owner()->get_pid()) + ")"; +} +bool ObjectAccessSimcallObserver::is_visible() const +{ + return get_owner() != get_issuer(); +} +ActorImpl* ObjectAccessSimcallObserver::get_owner() const +{ + return object_->simcall_owner_; +} + } // namespace simgrid::kernel::actor diff --git a/src/kernel/actor/SimcallObserver.hpp b/src/kernel/actor/SimcallObserver.hpp index 5b47b31adb..cfc60a1957 100644 --- a/src/kernel/actor/SimcallObserver.hpp +++ b/src/kernel/actor/SimcallObserver.hpp @@ -125,6 +125,32 @@ public: s4u::ActorPtr get_other_actor() const { return other_; } double get_timeout() const { return timeout_; } }; + +class ObjectAccessSimcallObserver final : public SimcallObserver { + ObjectAccessSimcallItem* const object_; + +public: + ObjectAccessSimcallObserver(ActorImpl* actor, ObjectAccessSimcallItem* object) + : SimcallObserver(actor), object_(object) + { + } + void serialize(std::stringstream& stream) const override; + std::string to_string() const override; + bool is_visible() const override; + bool is_enabled() override { return true; } + + ActorImpl* get_owner() const; +}; + +/* Semi private template used by the to_string methods of various observer classes */ +template static std::string ptr_to_id(A* ptr) +{ + static std::unordered_map map; + if (map.find(ptr) == map.end()) + map.insert(std::make_pair(ptr, std::to_string(map.size() + 1))); + return map[ptr]; +} + } // namespace simgrid::kernel::actor #endif diff --git a/src/kernel/resource/Resource.hpp b/src/kernel/resource/Resource.hpp index ea32b925e2..32355aac24 100644 --- a/src/kernel/resource/Resource.hpp +++ b/src/kernel/resource/Resource.hpp @@ -7,6 +7,7 @@ #define SIMGRID_KERNEL_RESOURCE_RESOURCE_HPP #include "simgrid/forward.h" +#include "src/kernel/actor/Simcall.hpp" #include "src/kernel/lmm/maxmin.hpp" // Constraint #include "src/kernel/resource/profile/Event.hpp" #include "src/kernel/resource/profile/FutureEvtSet.hpp" @@ -23,7 +24,7 @@ namespace simgrid::kernel::resource { * @brief SURF resource interface class * @details This is the ancestor class of every resources in SimGrid, such as links, CPU or disk */ -class XBT_PUBLIC Resource { +class XBT_PUBLIC Resource : public actor::ObjectAccessSimcallItem { std::string name_ = "unnamed"; bool is_on_ = true; bool sealed_ = false; diff --git a/src/mc/compare.cpp b/src/mc/compare.cpp index fdeba59b63..c08b8f1338 100644 --- a/src/mc/compare.cpp +++ b/src/mc/compare.cpp @@ -8,6 +8,7 @@ #include "src/mc/mc_config.hpp" #include "src/mc/mc_private.hpp" #include "src/mc/sosp/Snapshot.hpp" +#include "xbt/ex.h" #include diff --git a/src/mc/transition/Transition.cpp b/src/mc/transition/Transition.cpp index b10abd263d..6b632eeaaa 100644 --- a/src/mc/transition/Transition.cpp +++ b/src/mc/transition/Transition.cpp @@ -4,6 +4,7 @@ * under the terms of the license (GNU LGPL) which comes with this package. */ #include "src/mc/transition/Transition.hpp" +#include "src/kernel/actor/Simcall.hpp" #include "xbt/asserts.h" #include "xbt/string.hpp" #include diff --git a/src/mc/transition/Transition.hpp b/src/mc/transition/Transition.hpp index 47a4b08a42..80a781838e 100644 --- a/src/mc/transition/Transition.hpp +++ b/src/mc/transition/Transition.hpp @@ -31,7 +31,8 @@ class Transition { public: /* Ordering is important here. depends() implementations only consider subsequent types in this ordering */ - XBT_DECLARE_ENUM_CLASS(Type, RANDOM, ACTOR_JOIN, /* First because indep with anybody */ + XBT_DECLARE_ENUM_CLASS(Type, RANDOM, ACTOR_JOIN, /* First because indep with anybody including themselves */ + OBJECT_ACCESS, /* high priority because indep with almost everybody */ TESTANY, WAITANY, /* high priority because they can rewrite themselves to *_WAIT */ BARRIER_ASYNC_LOCK, BARRIER_WAIT, /* BARRIER transitions sorted alphabetically */ COMM_ASYNC_RECV, COMM_ASYNC_SEND, COMM_TEST, COMM_WAIT, /* Alphabetical ordering of COMM_* */ diff --git a/src/s4u/s4u_ConditionVariable.cpp b/src/s4u/s4u_ConditionVariable.cpp index e7e772465a..b04a2f38c2 100644 --- a/src/s4u/s4u_ConditionVariable.cpp +++ b/src/s4u/s4u_ConditionVariable.cpp @@ -7,6 +7,7 @@ #include #include +#include "src/kernel/activity/ActivityImpl.hpp" #include "src/kernel/activity/ConditionVariableImpl.hpp" #include "src/kernel/actor/SimcallObserver.hpp" diff --git a/src/s4u/s4u_Disk.cpp b/src/s4u/s4u_Disk.cpp index fa64f72061..c10ad5a722 100644 --- a/src/s4u/s4u_Disk.cpp +++ b/src/s4u/s4u_Disk.cpp @@ -31,13 +31,13 @@ const char* Disk::get_cname() const Disk* Disk::set_read_bandwidth(double read_bw) { - kernel::actor::simcall_answered([this, read_bw] { pimpl_->set_read_bandwidth(read_bw); }); + kernel::actor::simcall_object_access(pimpl_, [this, read_bw] { pimpl_->set_read_bandwidth(read_bw); }); return this; } Disk* Disk::set_write_bandwidth(double write_bw) { - kernel::actor::simcall_answered([this, write_bw] { pimpl_->set_write_bandwidth(write_bw); }); + kernel::actor::simcall_object_access(pimpl_, [this, write_bw] { pimpl_->set_write_bandwidth(write_bw); }); return this; } @@ -48,7 +48,7 @@ double Disk::get_read_bandwidth() const Disk* Disk::set_readwrite_bandwidth(double bw) { - kernel::actor::simcall_answered([this, bw] { pimpl_->set_readwrite_bandwidth(bw); }); + kernel::actor::simcall_object_access(pimpl_, [this, bw] { pimpl_->set_readwrite_bandwidth(bw); }); return this; } @@ -80,34 +80,36 @@ const char* Disk::get_property(const std::string& key) const Disk* Disk::set_property(const std::string& key, const std::string& value) { - kernel::actor::simcall_answered([this, &key, &value] { this->pimpl_->set_property(key, value); }); + kernel::actor::simcall_object_access(pimpl_, [this, &key, &value] { this->pimpl_->set_property(key, value); }); return this; } Disk* Disk::set_properties(const std::unordered_map& properties) { - kernel::actor::simcall_answered([this, properties] { this->pimpl_->set_properties(properties); }); + kernel::actor::simcall_object_access(pimpl_, [this, properties] { this->pimpl_->set_properties(properties); }); return this; } Disk* Disk::set_state_profile(kernel::profile::Profile* profile) { xbt_assert(not pimpl_->is_sealed(), "Cannot set a state profile once the Disk is sealed"); - kernel::actor::simcall_answered([this, profile]() { this->pimpl_->set_state_profile(profile); }); + kernel::actor::simcall_object_access(pimpl_, [this, profile]() { this->pimpl_->set_state_profile(profile); }); return this; } Disk* Disk::set_read_bandwidth_profile(kernel::profile::Profile* profile) { xbt_assert(not pimpl_->is_sealed(), "Cannot set a bandwidth profile once the Disk is sealed"); - kernel::actor::simcall_answered([this, profile]() { this->pimpl_->set_read_bandwidth_profile(profile); }); + kernel::actor::simcall_object_access(pimpl_, + [this, profile]() { this->pimpl_->set_read_bandwidth_profile(profile); }); return this; } Disk* Disk::set_write_bandwidth_profile(kernel::profile::Profile* profile) { xbt_assert(not pimpl_->is_sealed(), "Cannot set a bandwidth profile once the Disk is sealed"); - kernel::actor::simcall_answered([this, profile]() { this->pimpl_->set_write_bandwidth_profile(profile); }); + kernel::actor::simcall_object_access(pimpl_, + [this, profile]() { this->pimpl_->set_write_bandwidth_profile(profile); }); return this; } @@ -156,7 +158,7 @@ sg_size_t Disk::write(sg_size_t size, double priority) const Disk* Disk::set_sharing_policy(Disk::Operation op, Disk::SharingPolicy policy, const NonLinearResourceCb& cb) { - kernel::actor::simcall_answered([this, op, policy, &cb] { pimpl_->set_sharing_policy(op, policy, cb); }); + kernel::actor::simcall_object_access(pimpl_, [this, op, policy, &cb] { pimpl_->set_sharing_policy(op, policy, cb); }); return this; } @@ -167,7 +169,7 @@ Disk::SharingPolicy Disk::get_sharing_policy(Operation op) const Disk* Disk::set_factor_cb(const std::function& cb) { - kernel::actor::simcall_answered([this, &cb] { pimpl_->set_factor_cb(cb); }); + kernel::actor::simcall_object_access(pimpl_, [this, &cb] { pimpl_->set_factor_cb(cb); }); return this; } diff --git a/src/s4u/s4u_Exec.cpp b/src/s4u/s4u_Exec.cpp index 286340f938..24946989ec 100644 --- a/src/s4u/s4u_Exec.cpp +++ b/src/s4u/s4u_Exec.cpp @@ -3,6 +3,7 @@ /* This program is free software; you can redistribute it and/or modify it * under the terms of the license (GNU LGPL) which comes with this package. */ +#include "simgrid/simix.hpp" #include #include #include @@ -74,8 +75,9 @@ ExecPtr Exec::set_bound(double bound) { xbt_assert(state_ == State::INITED || state_ == State::STARTING, "Cannot change the bound of an exec after its start"); - kernel::actor::simcall_answered( - [this, bound] { boost::static_pointer_cast(pimpl_)->set_bound(bound); }); + kernel::actor::simcall_object_access(pimpl_.get(), [this, bound] { + boost::static_pointer_cast(pimpl_)->set_bound(bound); + }); return this; } @@ -89,7 +91,7 @@ ExecPtr Exec::set_priority(double priority) { xbt_assert(state_ == State::INITED || state_ == State::STARTING, "Cannot change the priority of an exec after its start"); - kernel::actor::simcall_answered([this, priority] { + kernel::actor::simcall_object_access(pimpl_.get(), [this, priority] { boost::static_pointer_cast(pimpl_)->set_sharing_penalty(1. / priority); }); return this; @@ -107,7 +109,7 @@ ExecPtr Exec::set_flops_amount(double flops_amount) { xbt_assert(state_ == State::INITED || state_ == State::STARTING, "Cannot change the flop_amount of an exec after its start"); - kernel::actor::simcall_answered([this, flops_amount] { + kernel::actor::simcall_object_access(pimpl_.get(), [this, flops_amount] { boost::static_pointer_cast(pimpl_)->set_flops_amount(flops_amount); }); set_remaining(flops_amount); @@ -118,7 +120,7 @@ ExecPtr Exec::set_flops_amounts(const std::vector& flops_amounts) { xbt_assert(state_ == State::INITED || state_ == State::STARTING, "Cannot change the flops_amounts of an exec after its start"); - kernel::actor::simcall_answered([this, flops_amounts] { + kernel::actor::simcall_object_access(pimpl_.get(), [this, flops_amounts] { boost::static_pointer_cast(pimpl_)->set_flops_amounts(flops_amounts); }); parallel_ = true; @@ -129,7 +131,7 @@ ExecPtr Exec::set_bytes_amounts(const std::vector& bytes_amounts) { xbt_assert(state_ == State::INITED || state_ == State::STARTING, "Cannot change the bytes_amounts of an exec after its start"); - kernel::actor::simcall_answered([this, bytes_amounts] { + kernel::actor::simcall_object_access(pimpl_.get(), [this, bytes_amounts] { boost::static_pointer_cast(pimpl_)->set_bytes_amounts(bytes_amounts); }); parallel_ = true; @@ -140,7 +142,7 @@ ExecPtr Exec::set_thread_count(int thread_count) { xbt_assert(state_ == State::INITED || state_ == State::STARTING, "Cannot change the bytes_amounts of an exec after its start"); - kernel::actor::simcall_answered([this, thread_count] { + kernel::actor::simcall_object_access(pimpl_.get(), [this, thread_count] { boost::static_pointer_cast(pimpl_)->set_thread_count(thread_count); }); return this; @@ -174,8 +176,8 @@ ExecPtr Exec::set_host(Host* host) if (state_ == State::STARTED) boost::static_pointer_cast(pimpl_)->migrate(host); - kernel::actor::simcall_answered( - [this, host] { boost::static_pointer_cast(pimpl_)->set_host(host); }); + kernel::actor::simcall_object_access( + pimpl_.get(), [this, host] { boost::static_pointer_cast(pimpl_)->set_host(host); }); if (state_ == State::STARTING) // Setting the host may allow to start the activity, let's try @@ -189,8 +191,9 @@ ExecPtr Exec::set_hosts(const std::vector& hosts) xbt_assert(state_ == State::INITED || state_ == State::STARTING, "Cannot change the hosts of an exec once it's done (state: %s)", to_c_str(state_)); - kernel::actor::simcall_answered( - [this, hosts] { boost::static_pointer_cast(pimpl_)->set_hosts(hosts); }); + kernel::actor::simcall_object_access(pimpl_.get(), [this, hosts] { + boost::static_pointer_cast(pimpl_)->set_hosts(hosts); + }); parallel_ = true; // Setting the host may allow to start the activity, let's try diff --git a/src/s4u/s4u_Host.cpp b/src/s4u/s4u_Host.cpp index 4266d80987..6f80d6e1f1 100644 --- a/src/s4u/s4u_Host.cpp +++ b/src/s4u/s4u_Host.cpp @@ -13,6 +13,7 @@ #include #include +#include "simgrid/simix.hpp" #include "src/kernel/resource/StandardLinkImpl.hpp" #include "src/kernel/resource/VirtualMachineImpl.hpp" #include "src/surf/HostImpl.hpp" @@ -196,13 +197,13 @@ const char* Host::get_property(const std::string& key) const Host* Host::set_property(const std::string& key, const std::string& value) { - kernel::actor::simcall_answered([this, &key, &value] { this->pimpl_->set_property(key, value); }); + kernel::actor::simcall_object_access(pimpl_, [this, &key, &value] { this->pimpl_->set_property(key, value); }); return this; } Host* Host::set_properties(const std::unordered_map& properties) { - kernel::actor::simcall_answered([this, &properties] { this->pimpl_->set_properties(properties); }); + kernel::actor::simcall_object_access(pimpl_, [this, &properties] { this->pimpl_->set_properties(properties); }); return this; } @@ -210,7 +211,7 @@ Host* Host::set_properties(const std::unordered_map& p * The profile must contain boolean values. */ Host* Host::set_state_profile(kernel::profile::Profile* p) { - kernel::actor::simcall_answered([this, p] { pimpl_cpu_->set_state_profile(p); }); + kernel::actor::simcall_object_access(pimpl_, [this, p] { pimpl_cpu_->set_state_profile(p); }); return this; } /** Specify a profile modeling the external load according to an exhaustive list or a stochastic law. @@ -221,7 +222,7 @@ Host* Host::set_state_profile(kernel::profile::Profile* p) */ Host* Host::set_speed_profile(kernel::profile::Profile* p) { - kernel::actor::simcall_answered([this, p] { pimpl_cpu_->set_speed_profile(p); }); + kernel::actor::simcall_object_access(pimpl_, [this, p] { pimpl_cpu_->set_speed_profile(p); }); return this; } @@ -246,7 +247,7 @@ double Host::get_available_speed() const Host* Host::set_sharing_policy(SharingPolicy policy, const s4u::NonLinearResourceCb& cb) { - kernel::actor::simcall_answered([this, policy, &cb] { pimpl_cpu_->set_sharing_policy(policy, cb); }); + kernel::actor::simcall_object_access(pimpl_, [this, policy, &cb] { pimpl_cpu_->set_sharing_policy(policy, cb); }); return this; } @@ -262,13 +263,14 @@ int Host::get_core_count() const Host* Host::set_core_count(int core_count) { - kernel::actor::simcall_answered([this, core_count] { this->pimpl_cpu_->set_core_count(core_count); }); + kernel::actor::simcall_object_access(pimpl_, [this, core_count] { this->pimpl_cpu_->set_core_count(core_count); }); return this; } Host* Host::set_pstate_speed(const std::vector& speed_per_state) { - kernel::actor::simcall_answered([this, &speed_per_state] { pimpl_cpu_->set_pstate_speed(speed_per_state); }); + kernel::actor::simcall_object_access(pimpl_, + [this, &speed_per_state] { pimpl_cpu_->set_pstate_speed(speed_per_state); }); return this; } @@ -296,7 +298,7 @@ Host* Host::set_pstate_speed(const std::vector& speed_per_state) /** @brief Set the pstate at which the host should run */ Host* Host::set_pstate(unsigned long pstate_index) { - kernel::actor::simcall_answered([this, pstate_index] { this->pimpl_cpu_->set_pstate(pstate_index); }); + kernel::actor::simcall_object_access(pimpl_, [this, pstate_index] { this->pimpl_cpu_->set_pstate(pstate_index); }); return this; } @@ -308,14 +310,14 @@ unsigned long Host::get_pstate() const Host* Host::set_factor_cb(const std::function& cb) { - kernel::actor::simcall_answered([this, &cb] { pimpl_cpu_->set_factor_cb(cb); }); + kernel::actor::simcall_object_access(pimpl_, [this, &cb] { pimpl_cpu_->set_factor_cb(cb); }); return this; } Host* Host::set_coordinates(const std::string& coords) { if (not coords.empty()) - kernel::actor::simcall_answered([this, coords] { this->pimpl_netpoint_->set_coordinates(coords); }); + kernel::actor::simcall_object_access(pimpl_, [this, coords] { this->pimpl_netpoint_->set_coordinates(coords); }); return this; } std::vector Host::get_disks() const diff --git a/src/s4u/s4u_Io.cpp b/src/s4u/s4u_Io.cpp index 6d34be90e0..a463ce13f5 100644 --- a/src/s4u/s4u_Io.cpp +++ b/src/s4u/s4u_Io.cpp @@ -48,12 +48,11 @@ IoPtr Io::set_source(Host* from, Disk* from_disk) { xbt_assert(state_ == State::INITED || state_ == State::STARTING, "Cannot change the source of an IO stream once it's started (state: %s)", to_c_str(state_)); - kernel::actor::simcall_answered( - [this, from, from_disk] { - boost::static_pointer_cast(pimpl_)->set_host(from); - if (from_disk != nullptr) - boost::static_pointer_cast(pimpl_)->set_disk(from_disk->get_impl()); - }); + kernel::actor::simcall_object_access(pimpl_.get(), [this, from, from_disk] { + boost::static_pointer_cast(pimpl_)->set_host(from); + if (from_disk != nullptr) + boost::static_pointer_cast(pimpl_)->set_disk(from_disk->get_impl()); + }); // Setting 'source' may allow to start the activity, let's try if (state_ == State::STARTING && remains_ <= 0) XBT_DEBUG("This IO has a size of 0 byte. It cannot start yet"); @@ -67,12 +66,11 @@ IoPtr Io::set_destination(Host* to, Disk* to_disk) { xbt_assert(state_ == State::INITED || state_ == State::STARTING, "Cannot change the source of an IO stream once it's started (state: %s)", to_c_str(state_)); - kernel::actor::simcall_answered( - [this, to, to_disk] { - boost::static_pointer_cast(pimpl_)->set_dst_host(to); - if (to_disk != nullptr) - boost::static_pointer_cast(pimpl_)->set_dst_disk(to_disk->get_impl()); - }); + kernel::actor::simcall_object_access(pimpl_.get(), [this, to, to_disk] { + boost::static_pointer_cast(pimpl_)->set_dst_host(to); + if (to_disk != nullptr) + boost::static_pointer_cast(pimpl_)->set_dst_disk(to_disk->get_impl()); + }); // Setting 'destination' may allow to start the activity, let's try if (state_ == State::STARTING && remains_ <= 0) XBT_DEBUG("This IO has a size of 0 byte. It cannot start yet"); @@ -121,7 +119,7 @@ IoPtr Io::set_priority(double priority) { xbt_assert(state_ == State::INITED || state_ == State::STARTING, "Cannot change the priority of an io after its start"); - kernel::actor::simcall_answered([this, priority] { + kernel::actor::simcall_object_access(pimpl_.get(), [this, priority] { boost::static_pointer_cast(pimpl_)->set_sharing_penalty(1. / priority); }); return this; @@ -130,8 +128,8 @@ IoPtr Io::set_priority(double priority) IoPtr Io::set_size(sg_size_t size) { xbt_assert(state_ == State::INITED || state_ == State::STARTING, "Cannot set size once the Io is started"); - kernel::actor::simcall_answered( - [this, size] { boost::static_pointer_cast(pimpl_)->set_size(size); }); + kernel::actor::simcall_object_access( + pimpl_.get(), [this, size] { boost::static_pointer_cast(pimpl_)->set_size(size); }); set_remaining(size); return this; } @@ -139,8 +137,8 @@ IoPtr Io::set_size(sg_size_t size) IoPtr Io::set_op_type(OpType type) { xbt_assert(state_ == State::INITED || state_ == State::STARTING, "Cannot set size once the Io is started"); - kernel::actor::simcall_answered( - [this, type] { boost::static_pointer_cast(pimpl_)->set_type(type); }); + kernel::actor::simcall_object_access( + pimpl_.get(), [this, type] { boost::static_pointer_cast(pimpl_)->set_type(type); }); return this; } @@ -155,8 +153,8 @@ IoPtr Io::update_priority(double priority) /** @brief Returns the amount of flops that remain to be done */ double Io::get_remaining() const { - return kernel::actor::simcall_answered( - [this]() { return boost::static_pointer_cast(pimpl_)->get_remaining(); }); + return kernel::actor::simcall_object_access( + pimpl_.get(), [this]() { return boost::static_pointer_cast(pimpl_)->get_remaining(); }); } sg_size_t Io::get_performed_ioops() const diff --git a/src/s4u/s4u_Link.cpp b/src/s4u/s4u_Link.cpp index c1d4ffb204..a35cd13012 100644 --- a/src/s4u/s4u_Link.cpp +++ b/src/s4u/s4u_Link.cpp @@ -72,7 +72,7 @@ double Link::get_latency() const Link* Link::set_latency(double value) { - kernel::actor::simcall_answered([this, value] { pimpl_->set_latency(value); }); + kernel::actor::simcall_object_access(pimpl_, [this, value] { pimpl_->set_latency(value); }); return this; } @@ -95,7 +95,7 @@ double Link::get_bandwidth() const Link* Link::set_bandwidth(double value) { - kernel::actor::simcall_answered([this, value] { pimpl_->set_bandwidth(value); }); + kernel::actor::simcall_object_access(pimpl_, [this, value] { pimpl_->set_bandwidth(value); }); return this; } @@ -105,7 +105,7 @@ Link* Link::set_sharing_policy(Link::SharingPolicy policy, const NonLinearResour throw std::invalid_argument(std::string("Impossible to set wifi or split-duplex for the link: ") + get_name() + std::string(". Use appropriate create function in NetZone.")); - kernel::actor::simcall_answered([this, policy, &cb] { pimpl_->set_sharing_policy(policy, cb); }); + kernel::actor::simcall_object_access(pimpl_, [this, policy, &cb] { pimpl_->set_sharing_policy(policy, cb); }); return this; } Link::SharingPolicy Link::get_sharing_policy() const @@ -122,7 +122,7 @@ void Link::set_host_wifi_rate(const s4u::Host* host, int level) const Link* Link::set_concurrency_limit(int limit) { - kernel::actor::simcall_answered([this, limit] { pimpl_->set_concurrency_limit(limit); }); + kernel::actor::simcall_object_access(pimpl_, [this, limit] { pimpl_->set_concurrency_limit(limit); }); return this; } @@ -154,21 +154,21 @@ bool Link::is_on() const Link* Link::set_state_profile(kernel::profile::Profile* profile) { xbt_assert(not pimpl_->is_sealed(), "Cannot set a state profile once the Link is sealed"); - kernel::actor::simcall_answered([this, profile]() { this->pimpl_->set_state_profile(profile); }); + kernel::actor::simcall_object_access(pimpl_, [this, profile]() { this->pimpl_->set_state_profile(profile); }); return this; } Link* Link::set_bandwidth_profile(kernel::profile::Profile* profile) { xbt_assert(not pimpl_->is_sealed(), "Cannot set a bandwidth profile once the Link is sealed"); - kernel::actor::simcall_answered([this, profile]() { this->pimpl_->set_bandwidth_profile(profile); }); + kernel::actor::simcall_object_access(pimpl_, [this, profile]() { this->pimpl_->set_bandwidth_profile(profile); }); return this; } Link* Link::set_latency_profile(kernel::profile::Profile* profile) { xbt_assert(not pimpl_->is_sealed(), "Cannot set a latency profile once the Link is sealed"); - kernel::actor::simcall_answered([this, profile]() { this->pimpl_->set_latency_profile(profile); }); + kernel::actor::simcall_object_access(pimpl_, [this, profile]() { this->pimpl_->set_latency_profile(profile); }); return this; } @@ -178,7 +178,7 @@ const char* Link::get_property(const std::string& key) const } Link* Link::set_property(const std::string& key, const std::string& value) { - kernel::actor::simcall_answered([this, &key, &value] { this->pimpl_->set_property(key, value); }); + kernel::actor::simcall_object_access(pimpl_, [this, &key, &value] { this->pimpl_->set_property(key, value); }); return this; } @@ -189,7 +189,7 @@ const std::unordered_map* Link::get_properties() const Link* Link::set_properties(const std::unordered_map& properties) { - kernel::actor::simcall_answered([this, &properties] { this->pimpl_->set_properties(properties); }); + kernel::actor::simcall_object_access(pimpl_, [this, &properties] { this->pimpl_->set_properties(properties); }); return this; } diff --git a/src/s4u/s4u_VirtualMachine.cpp b/src/s4u/s4u_VirtualMachine.cpp index f607ad859c..46b643e780 100644 --- a/src/s4u/s4u_VirtualMachine.cpp +++ b/src/s4u/s4u_VirtualMachine.cpp @@ -3,6 +3,7 @@ /* This program is free software; you can redistribute it and/or modify it * under the terms of the license (GNU LGPL) which comes with this package. */ +#include "simgrid/simix.hpp" #include #include #include diff --git a/src/simix/libsmx.cpp b/src/simix/libsmx.cpp index 7929e52223..895bbadb1c 100644 --- a/src/simix/libsmx.cpp +++ b/src/simix/libsmx.cpp @@ -10,9 +10,12 @@ /* 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/config.h" +#include "simgrid/modelchecker.h" #include "src/kernel/EngineImpl.hpp" #include "src/kernel/activity/CommImpl.hpp" #include "src/kernel/actor/SimcallObserver.hpp" +#include "src/mc/mc_replay.hpp" #include #define SIMIX_H_NO_DEPRECATED_WARNING // avoid deprecation warning on include (remove with XBT_ATTRIB_DEPRECATED_v335) @@ -189,3 +192,28 @@ void simcall_run_blocking(std::function const& code, simgrid::kernel::ac // BUT simcall_answer IS NOT CALLED simcall(simgrid::kernel::actor::Simcall::Type::RUN_BLOCKING, code, observer); } + +void simcall_run_object_access(std::function const& code, simgrid::kernel::actor::ObjectAccessSimcallItem* item) +{ + auto self = simgrid::kernel::actor::ActorImpl::self(); + + // We only need a simcall if the order of the setters is important (parallel run or MC execution). + // Otherwise, just call the function with no simcall + + if (simgrid::kernel::context::is_parallel() +#if SIMGRID_HAVE_MC + || MC_is_active() || MC_record_replay_is_active() +#endif + ) { + + simgrid::kernel::actor::ObjectAccessSimcallObserver observer{self, item}; + simcall(simgrid::kernel::actor::Simcall::Type::RUN_ANSWERED, code, &observer); + item->take_ownership(); + } else { + // don't return from the context-switch we don't do + self->simcall_.call_ = simgrid::kernel::actor::Simcall::Type::RUN_BLOCKING; + self->simcall_.code_ = &code; + self->simcall_.observer_ = nullptr; + self->simcall_handle(0); + } +} \ No newline at end of file diff --git a/src/surf/HostImpl.hpp b/src/surf/HostImpl.hpp index 13c0772571..31841861f4 100644 --- a/src/surf/HostImpl.hpp +++ b/src/surf/HostImpl.hpp @@ -39,7 +39,7 @@ public: * @brief SURF Host interface class * @details A host represents a machine with an aggregation of a Cpu, a RoutingEdge and Disk(s) */ -class XBT_PRIVATE HostImpl : public xbt::PropertyHolder { +class XBT_PRIVATE HostImpl : public xbt::PropertyHolder, public actor::ObjectAccessSimcallItem { using ActorList = boost::intrusive::list, diff --git a/teshsuite/s4u/storage_client_server/storage_client_server.tesh b/teshsuite/s4u/storage_client_server/storage_client_server.tesh index f724c4d616..463c56c2ce 100644 --- a/teshsuite/s4u/storage_client_server/storage_client_server.tesh +++ b/teshsuite/s4u/storage_client_server/storage_client_server.tesh @@ -89,9 +89,9 @@ $ ./storage_client_server ${platfdir}/hosts_with_disks.xml "--log=root.fmt:[%10. > [ 1.207952] (server@alice) /tmp/titi.xml size: 654 bytes > [ 1.207952] (server@alice) /tmp/toto.xml size: 972 bytes > [ 1.207952] (server@alice) Disk1 is attached to alice +> [ 1.207952] (server@alice) Disk1 is attached to bob +> [ 1.207952] (server@alice) Disk2 is attached to bob > [ 1.207952] (client@bob) *** GET/SET DATA for disk: Disk1 *** > [ 1.207952] (client@bob) Get data: 'No User Data' > [ 1.207952] (client@bob) Set and get data: 'Some data' -> [ 1.207952] (server@alice) Disk1 is attached to bob -> [ 1.207952] (server@alice) Disk2 is attached to bob > [ 1.207952] (maestro@) Simulated time: 1.20795 -- 2.20.1