Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
rename the S4U method retrieving the internal impl to getImpl
[simgrid.git] / include / simgrid / s4u / actor.hpp
index a07c2bd..28dbdc4 100644 (file)
@@ -6,17 +6,29 @@
 #ifndef SIMGRID_S4U_ACTOR_HPP
 #define SIMGRID_S4U_ACTOR_HPP
 
+#include <atomic>
+#include <chrono>
+#include <functional>
+#include <memory>
 #include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <boost/intrusive_ptr.hpp>
+
 #include <xbt/base.h>
+#include <xbt/functional.hpp>
+
+#include <simgrid/chrono.hpp>
 #include <simgrid/simix.h>
 #include <simgrid/s4u/forward.hpp>
 
 namespace simgrid {
 namespace s4u {
 
-/** @addtogroup s4u_actor
- * 
- * @tableofcontents
+/** @ingroup s4u_api
  * 
  * An actor is an independent stream of execution in your distributed application.
  *
@@ -32,8 +44,6 @@ namespace s4u {
  * of this standard</a> may help to understand the philosophy of the S4U
  * Actors. 
  * 
- * (back to the @ref s4u_api "S4U documentation")
- * 
  * @section s4u_actor_def Defining the skeleton of an Actor
  * 
  * %As in the <a href="http://en.cppreference.com/w/cpp/thread">C++11
@@ -41,6 +51,8 @@ namespace s4u {
  * pure function or as an object. It is very simple with functions:
  * 
  * @code{.cpp}
+ * #include "s4u/actor.hpp"
+ * 
  * // Declare the code of your worker
  * void worker() {
  *   printf("Hello s4u");
@@ -48,7 +60,8 @@ namespace s4u {
  * };
  * 
  * // From your main or from another actor, create your actor on the host Jupiter
- * new Actor("worker", simgrid::s4u::Host::by_name("Jupiter"), worker);
+ * // The following line actually creates a new actor, even if there is no "new". 
+ * Actor("Alice", simgrid::s4u::Host::by_name("Jupiter"), worker);
  * @endcode
  * 
  * But some people prefer to encapsulate their actors in classes and
@@ -68,7 +81,7 @@ namespace s4u {
  * };
  * 
  * // From your main or from another actor, create your actor. Note the () after Worker
- * new Actor("worker", simgrid::s4u::Host::by_name("Jupiter"), Worker());
+ * Actor("Bob", simgrid::s4u::Host::by_name("Jupiter"), Worker());
  * @endcode
  * 
  * @section s4u_actor_flesh Fleshing your actor
@@ -114,18 +127,89 @@ namespace s4u {
  * 
  *  @{
  */
-   
+
 /** @brief Simulation Agent (see \ref s4u_actor)*/
 XBT_PUBLIC_CLASS Actor {
-  explicit Actor(smx_process_t smx_proc);
+  friend Mailbox;
+  friend simgrid::simix::Process;
+  smx_process_t pimpl_ = nullptr;
+
+  /** Wrap a (possibly non-copyable) single-use task into a `std::function` */
+  template<class F, class... Args>
+  static std::function<void()> wrap_task(F f, Args... args)
+  {
+    typedef decltype(f(std::move(args)...)) R;
+    auto task = std::make_shared<simgrid::xbt::Task<R()>>(
+      simgrid::xbt::makeTask(std::move(f), std::move(args)...));
+    return [=] {
+      (*task)();
+    };
+  }
+
+  Actor(smx_process_t pimpl) : pimpl_(pimpl) {}
+
 public:
-  Actor(const char* name, s4u::Host *host, double killTime, std::function<void()> code);
-  Actor(const char* name, s4u::Host *host, std::function<void()> code)
-    : Actor(name, host, -1, std::move(code)) {};
-  template<class C>
-  Actor(const char* name, s4u::Host *host, C code)
-    : Actor(name, host, -1, std::function<void()>(std::move(code))) {}
-  ~Actor();
+
+  // ***** No copy *****
+
+  Actor(Actor const&) = delete;
+  Actor& operator=(Actor const&) = delete;
+
+  // ***** Reference count (delegated to pimpl_) *****
+
+  friend void intrusive_ptr_add_ref(Actor* actor)
+  {
+    xbt_assert(actor != nullptr);
+    SIMIX_process_ref(actor->pimpl_);
+  }
+  friend void intrusive_ptr_release(Actor* actor)
+  {
+    xbt_assert(actor != nullptr);
+    SIMIX_process_unref(actor->pimpl_);
+  }
+  using Ptr = boost::intrusive_ptr<Actor>;
+
+  // ***** Actor creation *****
+
+  /** Create an actor using a function
+   *
+   *  If the actor is restarted, the actor has a fresh copy of the function.
+   */
+  static Ptr createActor(const char* name, s4u::Host *host, double killTime, std::function<void()> code);
+
+  static Ptr createActor(const char* name, s4u::Host *host, std::function<void()> code)
+  {
+    return createActor(name, host, -1.0, std::move(code));
+  }
+
+  /** Create an actor using code
+   *
+   *  Using this constructor, move-only type can be used. The consequence is
+   *  that we cannot copy the value and restart the process in its initial
+   *  state. In order to use auto-restart, an explicit `function` must be passed
+   *  instead.
+   */
+  template<class F, class... Args,
+    // This constructor is enabled only if the call code(args...) is valid:
+    typename = typename std::result_of<F(Args...)>::type
+    >
+  static Ptr createActor(const char* name, s4u::Host *host, F code, Args... args)
+  {
+    return createActor(name, host, wrap_task(std::move(code), std::move(args)...));
+  }
+
+  // Create actor from function name:
+
+  static Ptr createActor(const char* name, s4u::Host *host, double killTime,
+    const char* function, std::vector<std::string> args);
+
+  static Ptr createActor(const char* name, s4u::Host *host, const char* function,
+      std::vector<std::string> args)
+  {
+    return createActor(name, host, -1.0, function, std::move(args));
+  }
+
+  // ***** Methods *****
 
   /** Retrieves the actor that have the given PID (or NULL if not existing) */
   //static Actor *byPid(int pid); not implemented
@@ -152,6 +236,7 @@ public:
   void kill();
 
   static void kill(int pid);
+  static Ptr forPid(int pid);
   
   /**
    * Wait for the actor to finish.
@@ -163,17 +248,38 @@ public:
   /** Ask kindly to all actors to die. Only the issuer will survive. */
   static void killAll();
 
-protected:
-  smx_process_t getInferior() {return pimpl_;}
-private:
-  smx_process_t pimpl_ = nullptr;
+  /** Returns the internal implementation of this actor */
+  smx_process_t getImpl();
 };
 
-/** @brief Static methods working on the current actor (see @ref s4u_actor) */
+using ActorPtr = Actor::Ptr;
+
+/** @ingroup s4u_api
+ *  @brief Static methods working on the current actor (see @ref s4u::Actor) */
 namespace this_actor {
 
   /** Block the actor sleeping for that amount of seconds (may throws hostFailure) */
-  XBT_PUBLIC(void) sleep(double duration);
+  XBT_PUBLIC(void) sleep_for(double duration);
+  XBT_PUBLIC(void) sleep_until(double timeout);
+
+  template<class Rep, class Period>
+  inline void sleep_for(std::chrono::duration<Rep, Period> duration)
+  {
+    auto seconds = std::chrono::duration_cast<SimulationClockDuration>(duration);
+    this_actor::sleep_for(seconds.count());
+  }
+  template<class Duration>
+  inline void sleep_until(const SimulationTimePoint<Duration>& timeout_time)
+  {
+    auto timeout_native = std::chrono::time_point_cast<SimulationClockDuration>(timeout_time);
+    this_actor::sleep_until(timeout_native.time_since_epoch().count());
+  }
+
+  XBT_ATTRIB_DEPRECATED("Use sleep_for()")
+  inline void sleep(double duration)
+  {
+    return sleep_for(duration);
+  }
 
   /** Block the actor, computing the given amount of flops */
   XBT_PUBLIC(e_smx_state_t) execute(double flop);
@@ -189,6 +295,11 @@ namespace this_actor {
    * See \ref Comm for the full communication API (including non blocking communications).
   */
   XBT_PUBLIC(void) send(Mailbox &chan, void*payload, size_t simulatedSize);
+  
+  /**
+   * Return the PID of the current actor.
+   */
+  XBT_PUBLIC(int) getPid();
 
 };