diff --git a/include/asyncpp/core.h b/include/asyncpp/core.h index a441a81..9869530 100644 --- a/include/asyncpp/core.h +++ b/include/asyncpp/core.h @@ -3,7 +3,9 @@ #include "core/future.h" #include "core/result.h" #include "core/stream.h" +#include "core/task.h" #include "core/future.inl" #include "core/result.inl" #include "core/stream.inl" +#include "core/task.inl" diff --git a/include/asyncpp/core/task.h b/include/asyncpp/core/task.h new file mode 100644 index 0000000..02444af --- /dev/null +++ b/include/asyncpp/core/task.h @@ -0,0 +1,73 @@ +#pragma once + +#include + +namespace asyncpp +{ + + struct task + { + public: + /** + * @brief Destructor. + */ + virtual ~task() = default; + + /** + * @brief Poll the future stored in the task. + * + * @return TRUE if the task is finished, FALSE otherwise. + */ + bool poll(); + + private: + /** + * @brief Actual implementation of the poll function. + */ + virtual bool poll_impl() = 0; + + private: + struct storage + { + task* current { nullptr }; + }; + + /** + * @brief Get the thread local storage. + */ + static inline storage& local_storage(); + }; + + template + struct task_tpl + : public task + { + public: + using future_type = T_future; + + private: + future_type _future; + + public: + /** + * @brief Constructor. + */ + template + inline task_tpl(X_future&& p_future); + + private: + /** + * @brief Actual implementation of the poll function. + */ + inline bool poll_impl() override; + }; + + using task_ptr_s = std::shared_ptr; + + /** + * @brief Create a task from the passed future. + */ + template + inline task_ptr_s make_task(X_future&& p_future); + +} diff --git a/include/asyncpp/core/task.inl b/include/asyncpp/core/task.inl new file mode 100644 index 0000000..9b34143 --- /dev/null +++ b/include/asyncpp/core/task.inl @@ -0,0 +1,66 @@ +#pragma once + +namespace asyncpp +{ + + namespace __impl + { + + struct current_lock + { + task* owner; + task*& storage; + + current_lock( + task* p_owner, + task*& p_storage) + : owner (p_owner) + , storage (p_storage) + { + if (storage) + throw std::runtime_error("Thread local task instance is already assigned!"); + storage = owner; + } + + ~current_lock() + { storage = nullptr; } + }; + + } + + /* task */ + + bool task::poll() + { + __impl::current_lock l(this, local_storage().current); + return poll_impl(); + } + + task::storage& task::local_storage() + { + thread_local storage value; + return value; + } + + /* task_tpl */ + + template + template + task_tpl::task_tpl(X_future&& p_future) + : _future(std::forward(p_future)) + { } + + template + bool task_tpl::poll_impl() + { return _future.poll().is_ready(); } + + /* misc */ + + template + task_ptr_s make_task(X_future&& p_future) + { + using task_type = task_tpl; + return std::make_shared(std::forward(p_future)); + } + +} diff --git a/test/asyncpp/core/task_tests.cpp b/test/asyncpp/core/task_tests.cpp new file mode 100644 index 0000000..cd36d87 --- /dev/null +++ b/test/asyncpp/core/task_tests.cpp @@ -0,0 +1,48 @@ +#include + +#include + +using namespace ::testing; +using namespace ::asyncpp; + +struct delay +{ + int const delay { 5 }; + int count { 0 }; +}; + +namespace asyncpp +{ + + template<> + struct future_trait + : public future_base> + { + using value_type = int&; + + template + static inline auto poll(T_future& self) + { + using result_type = future_result; + + if (self.ref.count >= self.ref.delay) + return result_type::ready(self.ref.count); + + ++self.ref.count; + return result_type::not_ready(); + } + }; + +} + +TEST(task_tests, poll) +{ + auto t = make_task(as_future(delay { 5, 0 })); + + ASSERT_FALSE(t->poll()); + ASSERT_FALSE(t->poll()); + ASSERT_FALSE(t->poll()); + ASSERT_FALSE(t->poll()); + ASSERT_FALSE(t->poll()); + ASSERT_TRUE (t->poll()); +}