Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Change "if(...) xbt_die(...)" to "xbt_assert(...)".
[simgrid.git] / doc / doxygen / uhood_switch.doc
index bc6b8f3..4521722 100644 (file)
@@ -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
 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:
 commonly used by our users.
 
 So, the key ideas here are:
@@ -200,7 +200,7 @@ The crux of `future.then()` is:
 @code{cpp}
 template<class T>
 template<class F>
 @code{cpp}
 template<class T>
 template<class F>
-auto simgrid::kernel::Future<T>::thenNoUnwrap(F continuation)
+auto simgrid::kernel::Future<T>::then_no_unwrap(F continuation)
 -> Future<decltype(continuation(std::move(*this)))>
 {
   typedef decltype(continuation(std::move(*this))) R;
 -> Future<decltype(continuation(std::move(*this)))>
 {
   typedef decltype(continuation(std::move(*this))) R;
@@ -260,8 +260,7 @@ T simgrid::kernel::Future::get()
 template<class T>
 T simgrid::kernel::FutureState<T>::get()
 {
 template<class T>
 T simgrid::kernel::FutureState<T>::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_);
   status_ = FutureStatus::done;
   if (exception_) {
     std::exception_ptr exception = std::move(exception_);
@@ -304,14 +303,14 @@ as input. It looks like this:
 # This looks like C++ but it is a basic IDL-like language
 # (one definition per line) parsed by a python script:
 
 # This looks like C++ but it is a basic IDL-like language
 # (one definition per line) parsed by a python script:
 
-void process_kill(smx_process_t process);
+void process_kill(smx_actor_t process);
 void process_killall(int reset_pid);
 void process_killall(int reset_pid);
-void process_cleanup(smx_process_t process) [[nohandler]];
-void process_suspend(smx_process_t process) [[block]];
-void process_resume(smx_process_t process);
-void process_set_host(smx_process_t process, sg_host_t dest);
-int  process_is_suspended(smx_process_t process) [[nohandler]];
-int  process_join(smx_process_t process, double timeout) [[block]];
+void process_cleanup(smx_actor_t process) [[nohandler]];
+void process_suspend(smx_actor_t process) [[block]];
+void process_resume(smx_actor_t process);
+void process_set_host(smx_actor_t process, sg_host_t dest);
+int  process_is_suspended(smx_actor_t process) [[nohandler]];
+int  process_join(smx_actor_t process, double timeout) [[block]];
 int  process_sleep(double duration) [[block]];
 
 smx_mutex_t mutex_init();
 int  process_sleep(double duration) [[block]];
 
 smx_mutex_t mutex_init();
@@ -328,9 +327,9 @@ number and its arguments (among some other things):
 @code{cpp}
 struct s_smx_simcall {
   // Simcall number:
 @code{cpp}
 struct s_smx_simcall {
   // Simcall number:
-  e_smx_simcall_t call;
+  Simcall call;
   // Issuing actor:
   // Issuing actor:
-  smx_process_t issuer;
+  smx_actor_t issuer;
   // Arguments of the simcall:
   union u_smx_scalar args[11];
   // Result of the simcall:
   // Arguments of the simcall:
   union u_smx_scalar args[11];
   // Result of the simcall:
@@ -371,7 +370,7 @@ this generates a bunch of C++ files:
   responsible for wrapping the parameters in the `struct s_smx_simcall`;
   and wrapping out the result;
 
   responsible for wrapping the parameters in the `struct s_smx_simcall`;
   and wrapping out the result;
 
-* [accessors](https://github.com/simgrid/simgrid/blob/4ae2fd01d8cc55bf83654e29f294335e3cb1f022/src/simix/popping_accessors.h)
+* [accessors](https://github.com/simgrid/simgrid/blob/4ae2fd01d8cc55bf83654e29f294335e3cb1f022/src/simix/popping_accessors.hpp)
    to get/set values of of `struct s_smx_simcall`;
 
 * a simulation-kernel-side [big switch](https://github.com/simgrid/simgrid/blob/4ae2fd01d8cc55bf83654e29f294335e3cb1f022/src/simix/popping_generated.cpp#L106)
    to get/set values of of `struct s_smx_simcall`;
 
 * a simulation-kernel-side [big switch](https://github.com/simgrid/simgrid/blob/4ae2fd01d8cc55bf83654e29f294335e3cb1f022/src/simix/popping_generated.cpp#L106)
@@ -468,20 +467,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
 `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<T>`.  The
 simulator blocks the actor and resumes it when the `Future<T>` becomes
 ready with its result:
 
 @code{cpp}
 template<class F>
 immediately in the simulation kernel and returns a `Future<T>`.  The
 simulator blocks the actor and resumes it when the `Future<T>` becomes
 ready with its result:
 
 @code{cpp}
 template<class F>
-auto kernelSync(F code) -> decltype(code().get())
+auto kernel_sync(F code) -> decltype(code().get())
 {
   typedef decltype(code().get()) T;
 {
   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_process_t self = SIMIX_process_self();
+  smx_actor_t self = SIMIX_process_self();
   simgrid::xbt::Result<T> result;
 
   simcall_run_blocking([&result, self, &code]{
   simgrid::xbt::Result<T> result;
 
   simcall_run_blocking([&result, self, &code]{
@@ -510,7 +508,7 @@ auto kernelSync(F code) -> decltype(code().get())
 A contrived example of this would be:
 
 @code{cpp}
 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<void> future) {
       return 42;
   return kernel_wait_until(30).then(
     [](simgrid::kernel::Future<void> future) {
       return 42;
@@ -521,7 +519,7 @@ int res = simgrid::simix::kernelSync([&] {
 
 ### Asynchronous operations {#uhood_switch_v2_async}
 
 
 ### 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`)
 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 +549,7 @@ T simgrid::simix::Future<T>::get()
 {
   if (!valid())
     throw std::future_error(std::future_errc::no_state);
 {
   if (!valid())
     throw std::future_error(std::future_errc::no_state);
-  smx_process_t self = SIMIX_process_self();
+  smx_actor_t self = SIMIX_process_self();
   simgrid::xbt::Result<T> result;
   simcall_run_blocking([this, &result, self]{
     try {
   simgrid::xbt::Result<T> result;
   simcall_run_blocking([this, &result, self]{
     try {
@@ -572,12 +570,12 @@ T simgrid::simix::Future<T>::get()
 }
 @endcode
 
 }
 @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<class F>
 `simgrid::kernel::Future` into a `simgrid::simix::Future`:
 
 @code{cpp}
 template<class F>
-auto kernelAsync(F code)
+auto kernel_async(F code)
   -> Future<decltype(code().get())>
 {
   typedef decltype(code().get()) T;
   -> Future<decltype(code().get())>
 {
   typedef decltype(code().get()) T;
@@ -594,7 +592,7 @@ auto kernelAsync(F code)
 A contrived example of this would be:
 
 @code{cpp}
 A contrived example of this would be:
 
 @code{cpp}
-simgrid::simix::Future<int> future = simgrid::simix::kernelSync([&] {
+simgrid::simix::Future<int> future = simgrid::simix::kernel_sync([&] {
   return kernel_wait_until(30).then(
     [](simgrid::kernel::Future<void> future) {
       return 42;
   return kernel_wait_until(30).then(
     [](simgrid::kernel::Future<void> future) {
       return 42;
@@ -605,95 +603,22 @@ do_some_stuff();
 int res = future.get();
 @endcode
 
 int res = future.get();
 @endcode
 
-`kernelSync()` could be rewritten as:
+`kernel_sync()` could be rewritten as:
 
 @code{cpp}
 template<class F>
 
 @code{cpp}
 template<class F>
-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
 }
 @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
 
 `.get()`).
 
 ## Mutexes and condition variables
 
-## Mutexes
-
-SimGrid has had a C-based API for mutexes and condition variables for
-some time.  These mutexes are different from the standard
-system-level mutex (`std::mutex`, `pthread_mutex_t`, etc.) because
-they work at simulation-level.  Locking on a simulation mutex does
-not block the thread directly but makes a simcall
-(`simcall_mutex_lock()`) which asks the simulation kernel to wake the calling
-actor when it can get ownership of the mutex. Blocking directly at the
-OS level would deadlock the simulation.
-
-Reusing the C++ standard API for our simulation mutexes has many
-benefits:
-
- * it makes it easier for people familiar with the `std::mutex` to
-   understand and use SimGrid mutexes;
-
- * we can benefit from a proven API;
-
- * we can reuse from generic library code in SimGrid.
-
-We defined a reference-counted `Mutex` class for this (which supports
-the [`Lockable`](http://en.cppreference.com/w/cpp/concept/Lockable)
-requirements, see
-[`[thread.req.lockable.req]`](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf#page=1175)
-in the C++14 standard):
-
-@code{cpp}
-class Mutex {
-  friend ConditionVariable;
-private:
-  friend simgrid::simix::Mutex;
-  simgrid::simix::Mutex* mutex_;
-  Mutex(simgrid::simix::Mutex* mutex) : mutex_(mutex) {}
-public:
-
-  friend void intrusive_ptr_add_ref(Mutex* mutex);
-  friend void intrusive_ptr_release(Mutex* mutex);
-  using Ptr = boost::intrusive_ptr<Mutex>;
-
-  // No copy:
-  Mutex(Mutex const&) = delete;
-  Mutex& operator=(Mutex const&) = delete;
-
-  static Ptr createMutex();
-
-public:
-  void lock();
-  void unlock();
-  bool try_lock();
-};
-@endcode
-
-The methods are simply wrappers around existing simcalls:
-
-@code{cpp}
-void Mutex::lock()
-{
-  simcall_mutex_lock(mutex_);
-}
-@endcode
-
-Using the same API as `std::mutex` (`Lockable`) means we can use existing
-C++-standard code such as `std::unique_lock<Mutex>` or
-`std::lock_guard<Mutex>` for exception-safe mutex handling[^lock]:
-
-@code{cpp}
-{
-  std::lock_guard<simgrid::s4u::Mutex> lock(*mutex);
-  sum += 1;
-}
-@endcode
-
 ### Condition Variables
 
 Similarly SimGrid already had simulation-level condition variables
 ### Condition Variables
 
 Similarly SimGrid already had simulation-level condition variables
@@ -702,7 +627,7 @@ which can be exposed using the same API as `std::condition_variable`:
 @code{cpp}
 class ConditionVariable {
 private:
 @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:
   smx_cond_t cond_;
   ConditionVariable(smx_cond_t cond) : cond_(cond) {}
 public:
@@ -781,20 +706,15 @@ std::cv_status ConditionVariable::wait_for(
     simcall_cond_wait_timeout(cond_, lock.mutex()->mutex_, timeout);
     return std::cv_status::no_timeout;
   }
     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 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();
   }
   catch (...) {
     std::terminate();
@@ -842,7 +762,7 @@ 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.
 
 * 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
 expose asynchronous operations in the simulation kernel to the actors.
 
 In addition, we wrote variations of some other C++ standard library
@@ -867,7 +787,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
 
 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
 (cooperative scheduling): it implements cooperative/fiber
 [mutex](https://github.com/mozy/mordor/blob/4803b6343aee531bfc3588ffc26a0d0fdf14b274/mordor/fibersynchronization.h#L70),
 [recursive
@@ -888,30 +808,14 @@ single-object without shared-state and synchronisation:
 @code{cpp}
 template<class T>
 class Result {
 @code{cpp}
 template<class T>
 class Result {
-  enum class ResultStatus {
-    invalid,
-    value,
-    exception,
-  };
 public:
 public:
-  Result();
-  ~Result();
-  Result(Result const& that);
-  Result& operator=(Result const& that);
-  Result(Result&& that);
-  Result& operator=(Result&& that);
   bool is_valid() const;
   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:
   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<boost::blank, T, std::exception_ptr> value_;
 };
 @endcode~
 
 };
 @endcode~
 
@@ -1047,4 +951,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.
 
     in the simulation which we would like to avoid.
     `std::try_lock()` should be safe to use though.
 
-*/
\ No newline at end of file
+*/