Selaa lähdekoodia

review: process/scheduler (close #211)

Michele Caini 7 vuotta sitten
vanhempi
commit
9a001ebacc

+ 14 - 17
docs/process.md

@@ -40,11 +40,12 @@ class wants to _override_ the default behavior):
   least define it to work properly. The `void *` parameter is an opaque pointer
   least define it to work properly. The `void *` parameter is an opaque pointer
   to user data (if any) forwarded directly to the process during an update.
   to user data (if any) forwarded directly to the process during an update.
 
 
-* `void init(void *);`
+* `void init();`
 
 
-  It's invoked at the first tick, immediately before an update. The `void *`
-  parameter is an opaque pointer to user data (if any) forwarded directly to the
-  process during an update.
+  It's invoked when the process joins the running queue of a scheduler. This
+  happens as soon as it's attached to the scheduler if the process is a top
+  level one, otherwise when it replaces its parent if the process is a
+  continuation.
 
 
 * `void succeeded();`
 * `void succeeded();`
 
 
@@ -74,7 +75,7 @@ struct my_process: entt::process<my_process, std::uint32_t> {
     using delta_type = std::uint32_t;
     using delta_type = std::uint32_t;
 
 
     void update(delta_type delta, void *) {
     void update(delta_type delta, void *) {
-        remaining = delta > remaining ? delta_type{] : (remaining - delta);
+        remaining -= std::min(remaining, delta);
 
 
         // ...
         // ...
 
 
@@ -83,12 +84,8 @@ struct my_process: entt::process<my_process, std::uint32_t> {
         }
         }
     }
     }
 
 
-    void init(void *data) {
-        remaining = *static_cast<delta_type *>(data);
-    }
-
 private:
 private:
-    delta_type remaining;
+    delta_type remaining{1000u};
 };
 };
 ```
 ```
 
 
@@ -127,11 +124,11 @@ cycles.
 
 
 Each process is invoked once per tick. If it terminates, it's removed
 Each process is invoked once per tick. If it terminates, it's removed
 automatically from the scheduler and it's never invoked again. Otherwise it's
 automatically from the scheduler and it's never invoked again. Otherwise it's
-a good candidate to run once more the next tick.<br/>
-A process can also have a child. In this case, the process is replaced with
-its child when it terminates if it returns with success. In case of errors,
-both the process and its child are discarded. This way, it's easy to create
-chain of processes to run sequentially.
+a good candidate to run one more time the next tick.<br/>
+A process can also have a child. In this case, the parent process is replaced
+with its child when it terminates and only if it returns with success. In case
+of errors, both the parent process and its child are discarded. This way, it's
+easy to create chain of processes to run sequentially.
 
 
 Using a scheduler is straightforward. To create it, users must provide only the
 Using a scheduler is straightforward. To create it, users must provide only the
 type for the elapsed times and no arguments at all:
 type for the elapsed times and no arguments at all:
@@ -188,8 +185,8 @@ scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
 .then<my_process>();
 .then<my_process>();
 ```
 ```
 
 
-To update a scheduler and thus all its processes, the `update` member function
-is the way to go:
+To update a scheduler and therefore all its processes, the `update` member
+function is the way to go:
 
 
 ```cpp
 ```cpp
 // updates all the processes, no user data are provided
 // updates all the processes, no user data are provided

+ 11 - 9
src/entt/process/process.hpp

@@ -30,12 +30,13 @@ namespace entt {
  *   update.
  *   update.
  *
  *
  * * @code{.cpp}
  * * @code{.cpp}
- *   void init(void *);
+ *   void init();
  *   @endcode
  *   @endcode
  *
  *
- *   It's invoked at the first tick, immediately before an update. The `void *`
- *   parameter is an opaque pointer to user data (if any) forwarded directly to
- *   the process during an update.
+ *   It's invoked when the process joins the running queue of a scheduler. This
+ *   happens as soon as it's attached to the scheduler if the process is a top
+ *   level one, otherwise when it replaces its parent if the process is a
+ *   continuation.
  *
  *
  * * @code{.cpp}
  * * @code{.cpp}
  *   void succeeded();
  *   void succeeded();
@@ -84,9 +85,9 @@ class process {
     using state_value_t = std::integral_constant<state, value>;
     using state_value_t = std::integral_constant<state, value>;
 
 
     template<typename Target = Derived>
     template<typename Target = Derived>
-    auto tick(int, state_value_t<state::UNINITIALIZED>, void *data)
-    -> decltype(std::declval<Target>().init(data)) {
-        static_cast<Target *>(this)->init(data);
+    auto tick(int, state_value_t<state::UNINITIALIZED>)
+    -> decltype(std::declval<Target>().init()) {
+        static_cast<Target *>(this)->init();
     }
     }
 
 
     template<typename Target = Derived>
     template<typename Target = Derived>
@@ -232,11 +233,12 @@ public:
     void tick(const Delta delta, void *data = nullptr) {
     void tick(const Delta delta, void *data = nullptr) {
         switch (current) {
         switch (current) {
         case state::UNINITIALIZED:
         case state::UNINITIALIZED:
-            tick(0, state_value_t<state::UNINITIALIZED>{}, data);
+            tick(0, state_value_t<state::UNINITIALIZED>{});
             current = state::RUNNING;
             current = state::RUNNING;
-            [[fallthrough]];
+            break;
         case state::RUNNING:
         case state::RUNNING:
             tick(0, state_value_t<state::RUNNING>{}, delta, data);
             tick(0, state_value_t<state::RUNNING>{}, delta, data);
+            break;
         default:
         default:
             // suppress warnings
             // suppress warnings
             break;
             break;

+ 6 - 2
src/entt/process/scheduler.hpp

@@ -90,7 +90,8 @@ class scheduler {
         if(dead) {
         if(dead) {
             if(handler.next && !process->rejected()) {
             if(handler.next && !process->rejected()) {
                 handler = std::move(*handler.next);
                 handler = std::move(*handler.next);
-                dead = handler.update(handler, delta, data);
+                // forces the process to exit the uninitialized state
+                dead = handler.update(handler, {}, nullptr);
             } else {
             } else {
                 handler.instance.reset();
                 handler.instance.reset();
             }
             }
@@ -178,6 +179,8 @@ public:
         static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>);
         static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>);
         auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
         auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
         process_handler handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr};
         process_handler handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr};
+        // forces the process to exit the uninitialized state
+        handler.update(handler, {}, nullptr);
         return continuation{&handlers.emplace_back(std::move(handler))};
         return continuation{&handlers.emplace_back(std::move(handler))};
     }
     }
 
 
@@ -190,12 +193,13 @@ public:
      * following:
      * following:
      *
      *
      * @code{.cpp}
      * @code{.cpp}
-     * void(Delta delta, auto succeed, auto fail);
+     * void(Delta delta, void *data, auto succeed, auto fail);
      * @endcode
      * @endcode
      *
      *
      * Where:
      * Where:
      *
      *
      * * `delta` is the elapsed time.
      * * `delta` is the elapsed time.
+     * * `data` is an opaque pointer to user data if any, `nullptr` otherwise.
      * * `succeed` is a function to call when a process terminates with success.
      * * `succeed` is a function to call when a process terminates with success.
      * * `fail` is a function to call when a process terminates with errors.
      * * `fail` is a function to call when a process terminates with errors.
      *
      *

+ 9 - 3
test/entt/process/process.cpp

@@ -10,7 +10,7 @@ struct fake_process: entt::process<fake_process, int> {
     void pause() noexcept { process_type::pause(); }
     void pause() noexcept { process_type::pause(); }
     void unpause() noexcept { process_type::unpause(); }
     void unpause() noexcept { process_type::unpause(); }
 
 
-    void init(void *) { init_invoked = true; }
+    void init() { init_invoked = true; }
     void succeeded() { succeeded_invoked = true; }
     void succeeded() { succeeded_invoked = true; }
     void failed() { failed_invoked = true; }
     void failed() { failed_invoked = true; }
     void aborted() { aborted_invoked = true; }
     void aborted() { aborted_invoked = true; }
@@ -69,6 +69,7 @@ TEST(Process, Basics) {
 TEST(Process, Succeeded) {
 TEST(Process, Succeeded) {
     fake_process process;
     fake_process process;
 
 
+    process.tick(0);
     process.tick(0);
     process.tick(0);
     process.succeed();
     process.succeed();
     process.tick(0);
     process.tick(0);
@@ -87,6 +88,7 @@ TEST(Process, Succeeded) {
 TEST(Process, Fail) {
 TEST(Process, Fail) {
     fake_process process;
     fake_process process;
 
 
+    process.tick(0);
     process.tick(0);
     process.tick(0);
     process.fail();
     process.fail();
     process.tick(0);
     process.tick(0);
@@ -106,6 +108,7 @@ TEST(Process, Data) {
     fake_process process;
     fake_process process;
     int value = 0;
     int value = 0;
 
 
+    process.tick(0);
     process.tick(0, &value);
     process.tick(0, &value);
     process.succeed();
     process.succeed();
     process.tick(0, &value);
     process.tick(0, &value);
@@ -134,7 +137,7 @@ TEST(Process, AbortNextTick) {
     ASSERT_FALSE(process.paused());
     ASSERT_FALSE(process.paused());
 
 
     ASSERT_TRUE(process.init_invoked);
     ASSERT_TRUE(process.init_invoked);
-    ASSERT_TRUE(process.update_invoked);
+    ASSERT_FALSE(process.update_invoked);
     ASSERT_FALSE(process.succeeded_invoked);
     ASSERT_FALSE(process.succeeded_invoked);
     ASSERT_FALSE(process.failed_invoked);
     ASSERT_FALSE(process.failed_invoked);
     ASSERT_TRUE(process.aborted_invoked);
     ASSERT_TRUE(process.aborted_invoked);
@@ -151,7 +154,7 @@ TEST(Process, AbortImmediately) {
     ASSERT_FALSE(process.paused());
     ASSERT_FALSE(process.paused());
 
 
     ASSERT_TRUE(process.init_invoked);
     ASSERT_TRUE(process.init_invoked);
-    ASSERT_TRUE(process.update_invoked);
+    ASSERT_FALSE(process.update_invoked);
     ASSERT_FALSE(process.succeeded_invoked);
     ASSERT_FALSE(process.succeeded_invoked);
     ASSERT_FALSE(process.failed_invoked);
     ASSERT_FALSE(process.failed_invoked);
     ASSERT_TRUE(process.aborted_invoked);
     ASSERT_TRUE(process.aborted_invoked);
@@ -167,6 +170,7 @@ TEST(ProcessAdaptor, Resolved) {
 
 
     auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
     auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
 
 
+    process.tick(0);
     process.tick(0);
     process.tick(0);
 
 
     ASSERT_TRUE(process.dead());
     ASSERT_TRUE(process.dead());
@@ -183,6 +187,7 @@ TEST(ProcessAdaptor, Rejected) {
 
 
     auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
     auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
 
 
+    process.tick(0);
     process.tick(0);
     process.tick(0);
 
 
     ASSERT_TRUE(process.rejected());
     ASSERT_TRUE(process.rejected());
@@ -199,6 +204,7 @@ TEST(ProcessAdaptor, Data) {
 
 
     auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
     auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
 
 
+    process.tick(0);
     process.tick(0, &value);
     process.tick(0, &value);
 
 
     ASSERT_TRUE(process.dead());
     ASSERT_TRUE(process.dead());