@@ -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" |
@@ -0,0 +1,73 @@ | |||
#pragma once | |||
#include <memory> | |||
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<typename T_future> | |||
struct task_tpl | |||
: public task | |||
{ | |||
public: | |||
using future_type = T_future; | |||
private: | |||
future_type _future; | |||
public: | |||
/** | |||
* @brief Constructor. | |||
*/ | |||
template<typename X_future> | |||
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<task>; | |||
/** | |||
* @brief Create a task from the passed future. | |||
*/ | |||
template<typename X_future> | |||
inline task_ptr_s make_task(X_future&& p_future); | |||
} |
@@ -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<typename T_future> | |||
template<typename X_future> | |||
task_tpl<T_future>::task_tpl(X_future&& p_future) | |||
: _future(std::forward<X_future>(p_future)) | |||
{ } | |||
template<typename T_future> | |||
bool task_tpl<T_future>::poll_impl() | |||
{ return _future.poll().is_ready(); } | |||
/* misc */ | |||
template<typename X_future> | |||
task_ptr_s make_task(X_future&& p_future) | |||
{ | |||
using task_type = task_tpl<X_future>; | |||
return std::make_shared<task_type>(std::forward<X_future>(p_future)); | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
#include <gtest/gtest.h> | |||
#include <asyncpp.h> | |||
using namespace ::testing; | |||
using namespace ::asyncpp; | |||
struct delay | |||
{ | |||
int const delay { 5 }; | |||
int count { 0 }; | |||
}; | |||
namespace asyncpp | |||
{ | |||
template<> | |||
struct future_trait<delay, void> | |||
: public future_base<future<delay, void>> | |||
{ | |||
using value_type = int&; | |||
template<typename T_future> | |||
static inline auto poll(T_future& self) | |||
{ | |||
using result_type = future_result<value_type>; | |||
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()); | |||
} |