LCOV - code coverage report
Current view: top level - include/openmodelviewer/core/async - task_data.hpp (source / functions) Hit Total Coverage
Test: libopenmodelviewer coverage Lines: 44 45 97.8 %
Date: 2025-07-03 20:26:48 Functions: 24 27 88.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //
       2             : // Copyright 2024 OpenModelViewer Authors
       3             : // 
       4             : // Licensed under the Apache License, Version 2.0 (the "License");
       5             : // you may not use this file except in compliance with the License.
       6             : // You may obtain a copy of the License at
       7             : // 
       8             : //     http://www.apache.org/licenses/LICENSE-2.0
       9             : // 
      10             : // Unless required by applicable law or agreed to in writing, software
      11             : // distributed under the License is distributed on an "AS IS" BASIS,
      12             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13             : // See the License for the specific language governing permissions and
      14             : // limitations under the License.
      15             : //
      16             : 
      17             : #pragma once
      18             : 
      19             : #include "openmodelviewer/core/async/itask_data.hpp"
      20             : #include "openmodelviewer/core/async/task_result.hpp"
      21             : 
      22             : #include <atomic>
      23             : #include <optional>
      24             : #include <exception>
      25             : #include <stdexcept>
      26             : #include <mutex>
      27             : #include <functional>
      28             : #include <utility>
      29             : 
      30             : 
      31             : namespace openmodelviewer::core::async
      32             : {
      33             :         /**
      34             :          * @brief Stores the result, completion, and error state of an asynchronous task.
      35             :          *
      36             :          * This class is used internally by the ThreadPool to track the execution state
      37             :          * and return data of a task. It supports both successful resolution and
      38             :          * exception reporting. The result is stored in an optional container,
      39             :          * while the error is stored using std::exception_ptr.
      40             :          *
      41             :          * @tparam RType The result type produced by the task.
      42             :          */
      43             :         template <typename RType>
      44             :         class TaskData : public ITaskData
      45             :         {
      46             :         public:
      47             :                 /**
      48             :                  * @brief Returns whether the task has completed, either successfully or with an error.
      49             :                  *
      50             :                  * A task is considered completed if it has either returned a data
      51             :                  * or thrown an exception during execution.
      52             :                  */
      53         236 :                 inline bool completed() const noexcept override
      54             :                 {
      55         236 :                         return m_completed.load(std::memory_order_acquire);
      56             :                 }
      57             : 
      58             :                 /**
      59             :                  * @brief Returns whether the task ended with an error.
      60             :                  *
      61             :                  * @return true if the task failed with an exception.
      62             :                  */
      63          22 :                 inline bool errored() const noexcept override
      64             :                 {
      65          22 :                         return m_errored.load(std::memory_order_acquire);
      66             :                 }
      67             : 
      68             :                 /**
      69             :                  * @brief Returns the stored exception, if any.
      70             :                  *
      71             :                  * This can be used to rethrow the exception from another thread context.
      72             :                  *
      73             :                  * @return The exception_ptr representing the captured error.
      74             :                  */
      75           3 :                 inline std::exception_ptr exception() const noexcept override
      76             :                 {
      77           3 :                         return m_exception;
      78             :                 }
      79             : 
      80             :                 /**
      81             :                  * @brief Checks if a callback has been registered for this task.
      82             :                  *
      83             :                  * This method returns true if a callback function is set and ready to be invoked
      84             :                  * once the task completes. A registered callback will be automatically called
      85             :                  * with a TaskResult<RType> when the task finishes (successfully or with an error).
      86             :                  *
      87             :                  * @return true if a callback is registered; false otherwise.
      88             :                  */
      89          94 :                 inline bool hasCallback() const noexcept override
      90             :                 {
      91          94 :                         return m_callback != nullptr;
      92             :                 }
      93             : 
      94             :                 /**
      95             :                  * @brief Invokes the registered callback with the task result.
      96             :                  *
      97             :                  * This method constructs a TaskResult<RType> using the task�s current state,
      98             :                  * then calls the registered callback function if the task has completed and
      99             :                  * a callback is present. After execution, the callback is cleared.
     100             :                  *
     101             :                  * This should be called exactly once after the task has completed.
     102             :                  * If no callback is registered, nothing happens.
     103             :                  */
     104          97 :                 inline void invokeCallback() override
     105             :                 {
     106          97 :                         if (this->completed() && this->hasCallback())
     107             :                         {
     108         107 :                                 TaskResult<RType> tres =
     109             :                                 {
     110          28 :                                         .data = this->m_return,
     111          79 :                                         .error = this->m_exception
     112             :                                 };
     113          74 :                                 m_callback(tres);
     114          76 :                                 m_callback = nullptr;
     115          74 :                         }
     116          90 :                 }
     117             : 
     118             :                 /**
     119             :                  * @brief Stores the result and marks the task as successfully completed.
     120             :                  *
     121             :                  * This method should be called exactly once, by the executing thread.
     122             :                  *
     123             :                  * @param data The result produced by the task.
     124             :                  */
     125          87 :                 inline void resolve(RType data)
     126             :                 {
     127          87 :                         if (m_completed.load())
     128             :                         {
     129           1 :                                 throw std::runtime_error("Trying to resolve the same TaskData multiple times.");
     130             :                         }
     131             : 
     132          90 :                         m_return = std::move(data);
     133          89 :                         m_completed.store(true, std::memory_order_release);
     134          87 :                 }
     135             : 
     136             :                 /**
     137             :                  * @brief Reports an exception and marks the task as completed with an error.
     138             :                  *
     139             :                  * This method should be called if the task throws during execution.
     140             :                  *
     141             :                  * @param exception The exception_ptr representing the thrown error.
     142             :                  */
     143           9 :                 inline void report(std::exception_ptr exception)
     144             :                 {
     145           9 :                         m_completed.store(true, std::memory_order_release);
     146           9 :                         m_errored.store(true, std::memory_order_release);
     147           9 :                         m_exception = std::move(exception);
     148           9 :                 }
     149             : 
     150             :                 /**
     151             :                  * @brief Returns a const reference to the result data.
     152             :                  *
     153             :                  * The optional may be empty if the task is not completed,
     154             :                  * or if the task failed with an exception.
     155             :                  *
     156             :                  * @return A const reference to the result container.
     157             :                  */
     158          11 :                 inline const std::optional<RType>& retrieve() const noexcept
     159             :                 {
     160          11 :                         return m_return;
     161             :                 }
     162             : 
     163             :                 /**
     164             :                  * @brief Rethrows the exception if the task ended with an error.
     165             :                  *
     166             :                  * Use this to propagate exceptions in user code after checking completion.
     167             :                  * @throws The exception originally thrown by the task.
     168             :                  */
     169           3 :                 inline void rethrow() const
     170             :                 {
     171           3 :                         if (m_errored.load())
     172             :                         {
     173           3 :                                 std::rethrow_exception(m_exception);
     174             :                         }
     175           0 :                 }
     176             : 
     177             :                 /**
     178             :                  * @brief Stores a callback to be invoked when the task completes successfully or with an error.
     179             :                  *
     180             :                  * If the task has already completed at the time of registration, the callback is invoked
     181             :                  * immediately on the calling thread with the current TaskResult.
     182             :                  *
     183             :                  * After the callback is executed, it is cleared from memory to avoid redundant invocations.
     184             :                  *
     185             :                  * Thread-safe: protects internal state with a mutex to allow safe concurrent usage.
     186             :                  *
     187             :                  * @param callback A callable that takes a const reference to TaskResult<RType>.
     188             :                  */
     189          86 :                 inline void setCallback(std::function<void(const TaskResult<RType>&)> callback)
     190             :                 {
     191          86 :                         std::lock_guard lock(m_callbackMutex);
     192          86 :                         m_callback = std::move(callback);
     193             : 
     194          86 :                         if (m_completed)
     195             :                         {
     196           5 :                                 TaskResult<RType> result
     197             :                                 {
     198           1 :                                         .data = m_return,
     199           4 :                                         .error = m_exception
     200             :                                 };
     201             : 
     202           4 :                                 m_callback(result);
     203           4 :                                 m_callback = nullptr;
     204           4 :                         }
     205          86 :                 }
     206             : 
     207             :         private:
     208             :                 std::atomic<bool> m_errored{ false };
     209             :                 std::exception_ptr m_exception;
     210             :                 std::atomic<bool> m_completed{ false };
     211             :                 std::optional<RType> m_return;
     212             : 
     213             :                 std::mutex m_callbackMutex;
     214             :                 std::function<void(const TaskResult<RType>&)> m_callback;
     215             :         };
     216             : } // namespace openmodelviewer::core::async

Generated by: LCOV version 1.14