Jelajahi Sumber

review: process/scheduler (close #211)

Michele Caini 7 tahun lalu
induk
melakukan
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
   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();`
 
@@ -74,7 +75,7 @@ struct my_process: entt::process<my_process, std::uint32_t> {
     using delta_type = std::uint32_t;
 
     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:
-    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
 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
 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>();
 ```
 
-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
 // updates all the processes, no user data are provided

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

@@ -30,12 +30,13 @@ namespace entt {
  *   update.
  *
  * * @code{.cpp}
- *   void init(void *);
+ *   void init();
  *   @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}
  *   void succeeded();
@@ -84,9 +85,9 @@ class process {
     using state_value_t = std::integral_constant<state, value>;
 
     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>
@@ -232,11 +233,12 @@ public:
     void tick(const Delta delta, void *data = nullptr) {
         switch (current) {
         case state::UNINITIALIZED:
-            tick(0, state_value_t<state::UNINITIALIZED>{}, data);
+            tick(0, state_value_t<state::UNINITIALIZED>{});
             current = state::RUNNING;
-            [[fallthrough]];
+            break;
         case state::RUNNING:
             tick(0, state_value_t<state::RUNNING>{}, delta, data);
+            break;
         default:
             // suppress warnings
             break;

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

@@ -90,7 +90,8 @@ class scheduler {
         if(dead) {
             if(handler.next && !process->rejected()) {
                 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 {
                 handler.instance.reset();
             }
@@ -178,6 +179,8 @@ public:
         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>};
         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))};
     }
 
@@ -190,12 +193,13 @@ public:
      * following:
      *
      * @code{.cpp}
-     * void(Delta delta, auto succeed, auto fail);
+     * void(Delta delta, void *data, auto succeed, auto fail);
      * @endcode
      *
      * Where:
      *
      * * `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.
      * * `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 unpause() noexcept { process_type::unpause(); }
 
-    void init(void *) { init_invoked = true; }
+    void init() { init_invoked = true; }
     void succeeded() { succeeded_invoked = true; }
     void failed() { failed_invoked = true; }
     void aborted() { aborted_invoked = true; }
@@ -69,6 +69,7 @@ TEST(Process, Basics) {
 TEST(Process, Succeeded) {
     fake_process process;
 
+    process.tick(0);
     process.tick(0);
     process.succeed();
     process.tick(0);
@@ -87,6 +88,7 @@ TEST(Process, Succeeded) {
 TEST(Process, Fail) {
     fake_process process;
 
+    process.tick(0);
     process.tick(0);
     process.fail();
     process.tick(0);
@@ -106,6 +108,7 @@ TEST(Process, Data) {
     fake_process process;
     int value = 0;
 
+    process.tick(0);
     process.tick(0, &value);
     process.succeed();
     process.tick(0, &value);
@@ -134,7 +137,7 @@ TEST(Process, AbortNextTick) {
     ASSERT_FALSE(process.paused());
 
     ASSERT_TRUE(process.init_invoked);
-    ASSERT_TRUE(process.update_invoked);
+    ASSERT_FALSE(process.update_invoked);
     ASSERT_FALSE(process.succeeded_invoked);
     ASSERT_FALSE(process.failed_invoked);
     ASSERT_TRUE(process.aborted_invoked);
@@ -151,7 +154,7 @@ TEST(Process, AbortImmediately) {
     ASSERT_FALSE(process.paused());
 
     ASSERT_TRUE(process.init_invoked);
-    ASSERT_TRUE(process.update_invoked);
+    ASSERT_FALSE(process.update_invoked);
     ASSERT_FALSE(process.succeeded_invoked);
     ASSERT_FALSE(process.failed_invoked);
     ASSERT_TRUE(process.aborted_invoked);
@@ -167,6 +170,7 @@ TEST(ProcessAdaptor, Resolved) {
 
     auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
 
+    process.tick(0);
     process.tick(0);
 
     ASSERT_TRUE(process.dead());
@@ -183,6 +187,7 @@ TEST(ProcessAdaptor, Rejected) {
 
     auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
 
+    process.tick(0);
     process.tick(0);
 
     ASSERT_TRUE(process.rejected());
@@ -199,6 +204,7 @@ TEST(ProcessAdaptor, Data) {
 
     auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
 
+    process.tick(0);
     process.tick(0, &value);
 
     ASSERT_TRUE(process.dead());