X-Git-Url: http://bilbo.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/blobdiff_plain/d20f024dba9ff1e9c8822237caaf963b9e913889..501b1d3f4293a4a3c27ad38fd74e995584799576:/doc/doxygen/uhood_switch.doc diff --git a/doc/doxygen/uhood_switch.doc b/doc/doxygen/uhood_switch.doc index ccb9e72bc3..98d55a9c47 100644 --- a/doc/doxygen/uhood_switch.doc +++ b/doc/doxygen/uhood_switch.doc @@ -27,7 +27,7 @@ Mimicking the OS behavior may seem over-engineered here, but this is mandatory to the model-checker. The simcalls, representing actors' actions, are the transitions of the formal system. Verifying the system requires to manipulate these transitions explicitly. This also -allows to run safely the actors in parallel, even if this is less +allows one to run the actors safely in parallel, even if this is less commonly used by our users. So, the key ideas here are: @@ -109,7 +109,7 @@ simulation kernel. Our futures are based on the C++ Concurrency Technical Specification API, with a few differences: - - The simulation kernel is single-threaded so we do not need + - The simulation kernel is single-threaded so we do not need inter-thread synchronization for our futures. - As the simulation kernel cannot block, `f.wait()` is not meaningful @@ -200,7 +200,7 @@ The crux of `future.then()` is: @code{cpp} template template -auto simgrid::kernel::Future::thenNoUnwrap(F continuation) +auto simgrid::kernel::Future::then_no_unwrap(F continuation) -> Future { typedef decltype(continuation(std::move(*this))) R; @@ -260,8 +260,7 @@ T simgrid::kernel::Future::get() template T simgrid::kernel::FutureState::get() { - if (status_ != FutureStatus::ready) - xbt_die("Deadlock: this future is not ready"); + xbt_assert(status_ == FutureStatus::ready, "Deadlock: this future is not ready"); status_ = FutureStatus::done; if (exception_) { std::exception_ptr exception = std::move(exception_); @@ -280,7 +279,7 @@ So a simcall is a way for the actor to push a request to the simulation kernel and yield the control until the request is fulfilled. The performance requirements are very high because the actors usually do an inordinate amount of simcalls during the -simulation. +simulation. As for real syscalls, the basic idea is to write the wanted call and its arguments in a memory area that is specific to the actor, and @@ -328,7 +327,7 @@ number and its arguments (among some other things): @code{cpp} struct s_smx_simcall { // Simcall number: - e_smx_simcall_t call; + Simcall call; // Issuing actor: smx_actor_t issuer; // Arguments of the simcall: @@ -337,7 +336,6 @@ struct s_smx_simcall { union u_smx_scalar result; // Some additional stuff: smx_timer_t timer; - int mc_value; }; @endcode @@ -413,7 +411,7 @@ type and properly handles exceptions: @code{cpp} template -typename std::result_of::type kernelImmediate(F&& code) +typename std::result_of_t kernelImmediate(F&& code) { // If we are in the simulation kernel, we take the fast path and // execute the code directly without simcall @@ -423,7 +421,7 @@ typename std::result_of::type kernelImmediate(F&& code) // If we are in the application, pass the code to the simulation // kernel which executes it for us and reports the result: - typedef typename std::result_of::type R; + typedef typename std::result_of_t R; simgrid::xbt::Result result; simcall_run_kernel([&]{ xbt_assert(SIMIX_is_maestro(), "Not in maestro"); @@ -440,9 +438,9 @@ Example of usage: @code{cpp} xbt_dict_t Host::properties() { return simgrid::simix::kernelImmediate([&] { - simgrid::surf::HostImpl* surf_host = - this->extension(); - return surf_host->getProperties(); + simgrid::kernel::resource::HostImpl* host = + this->extension(); + return host->getProperties(); }); } @endcode @@ -468,20 +466,19 @@ kernel which will wake up the actor (with `simgrid::simix::unblock(actor)`) when the operation is completed. This is wrapped in a higher-level primitive as well. The -`kernelSync()` function expects a function-object which is executed +`kernel_sync()` function expects a function-object which is executed immediately in the simulation kernel and returns a `Future`. The simulator blocks the actor and resumes it when the `Future` becomes ready with its result: @code{cpp} template -auto kernelSync(F code) -> decltype(code().get()) +auto kernel_sync(F code) -> decltype(code().get()) { typedef decltype(code().get()) T; - if (SIMIX_is_maestro()) - xbt_die("Can't execute blocking call in kernel mode"); + xbt_assert(not SIMIX_is_maestro(), "Can't execute blocking call in kernel mode"); - smx_actor_t self = SIMIX_process_self(); + auto self = simgrid::kernel::actor::ActorImpl::self(); simgrid::xbt::Result result; simcall_run_blocking([&result, self, &code]{ @@ -510,7 +507,7 @@ auto kernelSync(F code) -> decltype(code().get()) A contrived example of this would be: @code{cpp} -int res = simgrid::simix::kernelSync([&] { +int res = simgrid::simix::kernel_sync([&] { return kernel_wait_until(30).then( [](simgrid::kernel::Future future) { return 42; @@ -521,7 +518,7 @@ int res = simgrid::simix::kernelSync([&] { ### Asynchronous operations {#uhood_switch_v2_async} -We can write the related `kernelAsync()` which wakes up the actor immediately +We can write the related `kernel_async()` which wakes up the actor immediately and returns a future to the actor. As this future is used in the actor context, it is a different future (`simgrid::simix::Future` instead of `simgrid::kernel::Future`) @@ -551,7 +548,7 @@ T simgrid::simix::Future::get() { if (!valid()) throw std::future_error(std::future_errc::no_state); - smx_actor_t self = SIMIX_process_self(); + auto self = simgrid::kernel::actor::ActorImpl::self(); simgrid::xbt::Result result; simcall_run_blocking([this, &result, self]{ try { @@ -572,12 +569,12 @@ T simgrid::simix::Future::get() } @endcode -`kernelAsync()` simply :wink: calls `kernelImmediate()` and wraps the +`kernel_async()` simply :wink: calls `kernelImmediate()` and wraps the `simgrid::kernel::Future` into a `simgrid::simix::Future`: @code{cpp} template -auto kernelAsync(F code) +auto kernel_async(F code) -> Future { typedef decltype(code().get()) T; @@ -594,7 +591,7 @@ auto kernelAsync(F code) A contrived example of this would be: @code{cpp} -simgrid::simix::Future future = simgrid::simix::kernelSync([&] { +simgrid::simix::Future future = simgrid::simix::kernel_sync([&] { return kernel_wait_until(30).then( [](simgrid::kernel::Future future) { return 42; @@ -605,18 +602,18 @@ do_some_stuff(); int res = future.get(); @endcode -`kernelSync()` could be rewritten as: +`kernel_sync()` could be rewritten as: @code{cpp} template -auto kernelSync(F code) -> decltype(code().get()) +auto kernel_sync(F code) -> decltype(code().get()) { - return kernelAsync(std::move(code)).get(); + return kernel_async(std::move(code)).get(); } @endcode The semantic is equivalent but this form would require two simcalls -instead of one to do the same job (one in `kernelAsync()` and one in +instead of one to do the same job (one in `kernel_async()` and one in `.get()`). ## Mutexes and condition variables @@ -629,7 +626,7 @@ which can be exposed using the same API as `std::condition_variable`: @code{cpp} class ConditionVariable { private: - friend s_smx_cond; + friend s_smx_cond_t; smx_cond_t cond_; ConditionVariable(smx_cond_t cond) : cond_(cond) {} public: @@ -678,7 +675,7 @@ public: void notify_one(); void notify_all(); - + }; @endcode @@ -708,20 +705,15 @@ std::cv_status ConditionVariable::wait_for( simcall_cond_wait_timeout(cond_, lock.mutex()->mutex_, timeout); return std::cv_status::no_timeout; } - catch (xbt_ex& e) { - + catch (const simgrid::TimeoutException& e) { // If the exception was a timeout, we have to take the lock again: - if (e.category == timeout_error) { - try { - lock.mutex()->lock(); - return std::cv_status::timeout; - } - catch (...) { - std::terminate(); - } + try { + lock.mutex()->lock(); + return std::cv_status::timeout; + } + catch (...) { + std::terminate(); } - - std::terminate(); } catch (...) { std::terminate(); @@ -754,7 +746,7 @@ bool ConditionVariable::wait_for(std::unique_lock& lock, double duration, P pred) { return this->wait_until(lock, - SIMIX_get_clock() + duration, std::move(pred)); + simgrid::s4u::Engine::get_clock() + duration, std::move(pred)); } @endcode @@ -769,13 +761,13 @@ We wrote two future implementations based on the `std::future` API: * the second one is a wait-based (`future.get()`) future used in the actors which waits using a simcall. -These futures are used to implement `kernelSync()` and `kernelAsync()` which +These futures are used to implement `kernel_sync()` and `kernel_async()` which expose asynchronous operations in the simulation kernel to the actors. In addition, we wrote variations of some other C++ standard library classes (`SimulationClock`, `Mutex`, `ConditionVariable`) which work in the simulation: - + * using simulated time; * using simcalls for synchronisation. @@ -794,7 +786,7 @@ Reusing the same API as the C++ standard library is very useful because: This type of approach might be useful for other libraries which define their own contexts. An example of this is -[Mordor](https://github.com/mozy/mordor), a I/O library using fibers +[Mordor](https://github.com/mozy/mordor), an I/O library using fibers (cooperative scheduling): it implements cooperative/fiber [mutex](https://github.com/mozy/mordor/blob/4803b6343aee531bfc3588ffc26a0d0fdf14b274/mordor/fibersynchronization.h#L70), [recursive @@ -815,30 +807,14 @@ single-object without shared-state and synchronisation: @code{cpp} template class Result { - enum class ResultStatus { - invalid, - value, - exception, - }; public: - Result(); - ~Result(); - Result(Result const& that); - Result& operator=(Result const& that); - Result(Result&& that); - Result& operator=(Result&& that); bool is_valid() const; - void reset(); void set_exception(std::exception_ptr e); void set_value(T&& value); void set_value(T const& value); T get(); private: - ResultStatus status_ = ResultStatus::invalid; - union { - T value_; - std::exception_ptr exception_; - }; + boost::variant value_; }; @endcode~ @@ -954,7 +930,7 @@ auto makeTask(F code, Args... args) @endcode -## Notes +## Notes [^getcompared]: @@ -974,4 +950,4 @@ auto makeTask(F code, Args... args) in the simulation which we would like to avoid. `std::try_lock()` should be safe to use though. -*/ \ No newline at end of file +*/