diff --git a/cmake/asyncpp-options.cmake b/cmake/asyncpp-options.cmake index 0eb8548..5e406d4 100644 --- a/cmake/asyncpp-options.cmake +++ b/cmake/asyncpp-options.cmake @@ -9,3 +9,6 @@ Option ( ASYNCPP_INSTALL_PACKAGE Option ( ASYNCPP_USE_GIT_VERSION "Read the git tags to get the version of asyncpp" ON ) +Option ( ASYNCPP_FEATURE_TIMING_ENABLED + "Enable the timing features of asyncpp" + ON ) diff --git a/cmake/config.h.in b/cmake/config.h.in new file mode 100644 index 0000000..85fe7b8 --- /dev/null +++ b/cmake/config.h.in @@ -0,0 +1,3 @@ +#pragma once + +#cmakedefine ASYNCPP_FEATURE_TIMING_ENABLED diff --git a/include/asyncpp.h b/include/asyncpp.h index 7e5f792..3e57fa6 100644 --- a/include/asyncpp.h +++ b/include/asyncpp.h @@ -1,3 +1,9 @@ #pragma once +#include + #include + +#ifdef ASYNCPP_FEATURE_TIMING_ENABLED + #include +#endif diff --git a/include/asyncpp/core/future.h b/include/asyncpp/core/future.h index 13b1ae8..706ef96 100644 --- a/include/asyncpp/core/future.h +++ b/include/asyncpp/core/future.h @@ -6,6 +6,6 @@ #include "future/lazy.inl" #include "future/and_then.inl" -#ifdef asyncpp_timing +#ifdef ASYNCPP_FEATURE_TIMING_ENABLED #include "future/timeout.inl" #endif diff --git a/include/asyncpp/core/future/future.h b/include/asyncpp/core/future/future.h index 2862d39..18ca90c 100644 --- a/include/asyncpp/core/future/future.h +++ b/include/asyncpp/core/future/future.h @@ -57,7 +57,7 @@ namespace asyncpp typename X_lambda> inline auto and_then(X_lambda&& p_lambda) &&; - #ifdef asyncpp_timing + #ifdef ASYNCPP_FEATURE_TIMING_ENABLED public: /** * @brief Throw an execption if the timeout has passed. diff --git a/include/asyncpp/core/future/timeout.inl b/include/asyncpp/core/future/timeout.inl index d9a72ad..5985fda 100644 --- a/include/asyncpp/core/future/timeout.inl +++ b/include/asyncpp/core/future/timeout.inl @@ -1,5 +1,7 @@ #pragma once +#include + #include "future.h" namespace asyncpp { @@ -22,11 +24,11 @@ namespace __future { derived_type const &, derived_type &>; using derived_forward_type = forward_type_t; - using timeout_type = asyncpp::timing::timeout; + using timeout_future_type = asyncpp::timing::timeout_future; auto& self = static_cast(p_self); - return timeout_type( + return timeout_future_type( std::forward(self), p_timeout); } diff --git a/include/asyncpp/core/stream.h b/include/asyncpp/core/stream.h index f70b0c4..e120ad9 100644 --- a/include/asyncpp/core/stream.h +++ b/include/asyncpp/core/stream.h @@ -6,6 +6,6 @@ #include "stream/flatten.inl" #include "stream/for_each.inl" -#ifdef asyncpp_timing +#ifdef ASYNCPP_FEATURE_TIMING_ENABLED #include "stream/timeout.inl" #endif diff --git a/include/asyncpp/core/stream/stream.h b/include/asyncpp/core/stream/stream.h index 038e98d..9b13b62 100644 --- a/include/asyncpp/core/stream/stream.h +++ b/include/asyncpp/core/stream/stream.h @@ -75,7 +75,7 @@ namespace asyncpp chaining_mode X_mode = move> inline auto flatten() &&; - #ifdef asyncpp_timing + #ifdef ASYNCPP_FEATURE_TIMING_ENABLED public: /** * @brief Throw an execption if the timeout has passed. diff --git a/include/asyncpp/core/stream/timeout.inl b/include/asyncpp/core/stream/timeout.inl index 8b9159f..7eda9d1 100644 --- a/include/asyncpp/core/stream/timeout.inl +++ b/include/asyncpp/core/stream/timeout.inl @@ -1,5 +1,7 @@ #pragma once +#include + #include "stream.h" namespace asyncpp { @@ -22,11 +24,11 @@ namespace __stream { derived_type const &, derived_type &>; using derived_forward_type = forward_type_t; - using timeout_type = timing::timeout; + using timeout_stream_type = timing::timeout_stream; auto& self = static_cast(p_self); - return timeout_type( + return timeout_stream_type( std::forward(self), p_timeout); } diff --git a/include/asyncpp/core/task/task.h b/include/asyncpp/core/task/task.h index a7f16fc..f7b01a8 100644 --- a/include/asyncpp/core/task/task.h +++ b/include/asyncpp/core/task/task.h @@ -59,6 +59,9 @@ namespace asyncpp */ inline lock(task::handle_ptr_s p_handle); + inline lock(lock &&) = delete; + inline lock(lock const &) = delete; + /** * @brief Destructor. */ diff --git a/include/asyncpp/core/task/task.inl b/include/asyncpp/core/task/task.inl index 3043d09..665f1f7 100644 --- a/include/asyncpp/core/task/task.inl +++ b/include/asyncpp/core/task/task.inl @@ -1,5 +1,7 @@ #pragma once +#include + #include "task.h" namespace asyncpp @@ -8,7 +10,12 @@ namespace asyncpp /* task::lock */ task::lock::lock(task::handle_ptr_s p_handle) - { local_storage().current = p_handle; } + { + auto& current = local_storage().current; + if (!current.expired()) + throw cppcore::invalid_operation_exception("Current task is already assigned!"); + current = p_handle; + } task::lock::~lock() { local_storage().current.reset(); } diff --git a/include/asyncpp/executor/executor.inl b/include/asyncpp/executor/executor.inl index d2ac892..720c466 100644 --- a/include/asyncpp/executor/executor.inl +++ b/include/asyncpp/executor/executor.inl @@ -1,5 +1,7 @@ #pragma once +#include + #include "executor.h" #include "task_handle.inl" @@ -33,7 +35,12 @@ namespace executor { /* lock */ executor::lock::lock(executor& p_current) - { executor::local_storage().current = &p_current; } + { + auto& current = executor::local_storage().current; + if (current) + throw cppcore::invalid_operation_exception("Current executor is already assigned!"); + current = &p_current; + } executor::lock::~lock() { executor::local_storage().current = nullptr; } diff --git a/include/asyncpp/timing.h b/include/asyncpp/timing.h index 47708b7..7472958 100644 --- a/include/asyncpp/timing.h +++ b/include/asyncpp/timing.h @@ -1,13 +1,8 @@ #pragma once -#define asyncpp_timing +#define ASYNCPP_FEATURE_TIMING_ENABLED #include "timing/delay.h" #include "timing/interval.h" #include "timing/timeout.h" #include "timing/timer.h" - -#include "timing/delay.inl" -#include "timing/interval.inl" -#include "timing/timeout.inl" -#include "timing/timer.inl" diff --git a/include/asyncpp/timing/delay.h b/include/asyncpp/timing/delay.h index 83609d2..b3dab2f 100644 --- a/include/asyncpp/timing/delay.h +++ b/include/asyncpp/timing/delay.h @@ -1,64 +1,5 @@ #pragma once -#include -#include +#include "delay/delay.h" -#include "timer.h" -#include "delay.pre.h" - -namespace asyncpp { -namespace timing { - - struct delay final - : public base_future - { - public: - using value_type = void; - using base_future_type = base_future; - using result_type = typename base_future_type::result_type; - - private: - time_point _deadline; - __impl::registration_ptr_w _registration; - - public: - /** - * @brief Constructor. Create a delay at the given deadline. - */ - inline delay(const time_point& p_deadline); - - /** - * @brief Constructor. Create a delay with the given duration. - */ - template - inline delay(const duration& p_duration); - - /** - * @brief Destructor. - */ - inline ~delay(); - - /** - * @brief Get the current deadline of the delay. - */ - inline time_point deadline() const; - - /** - * @brief Reset the delay to a new time point. - */ - inline void reset(const time_point& p_deadline); - - /** - * @brief Reset the delay to the given duration. - */ - template - inline void reset(const duration& p_duration); - - public: /* future */ - /** - * @brief Poll the result from the future. - */ - inline result_type poll(); - }; - -} } +#include "delay/delay.inl" diff --git a/include/asyncpp/timing/delay/delay.h b/include/asyncpp/timing/delay/delay.h new file mode 100644 index 0000000..bac3e09 --- /dev/null +++ b/include/asyncpp/timing/delay/delay.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +#include "delay.pre.h" +#include "../timer/timer.h" +#include "../timer/resource.h" + +namespace asyncpp { +namespace timing { + + struct delay final + : public base_future + { + public: + using value_type = void; + using base_future_type = base_future; + using result_type = typename base_future_type::result_type; + + private: + time_point _deadline; + __impl::resource_ptr_w _resource; + + public: + /** + * @brief Constructor. Create a delay at the given deadline. + */ + inline delay(const time_point& p_deadline); + + /** + * @brief Constructor. Create a delay with the given duration. + */ + template + inline delay(const duration& p_duration); + + /** + * @brief Destructor. + */ + inline ~delay(); + + /** + * @brief Get the current deadline of the delay. + */ + inline time_point deadline() const; + + /** + * @brief Reset the delay to a new time point. + */ + inline void reset(const time_point& p_deadline); + + /** + * @brief Reset the delay to the given duration. + */ + template + inline void reset(const duration& p_duration); + + public: /* future */ + /** + * @brief Poll the result from the future. + */ + inline result_type poll(); + }; + +} } diff --git a/include/asyncpp/timing/delay.inl b/include/asyncpp/timing/delay/delay.inl similarity index 66% rename from include/asyncpp/timing/delay.inl rename to include/asyncpp/timing/delay/delay.inl index 3ead3de..ace33a6 100644 --- a/include/asyncpp/timing/delay.inl +++ b/include/asyncpp/timing/delay/delay.inl @@ -5,7 +5,7 @@ #include "delay.h" -#include "timer.inl" +#include "../timer/timer_base.inl" namespace asyncpp { namespace timing { @@ -13,32 +13,32 @@ namespace timing { /* delay */ delay::delay(const time_point& p_deadline) - : _deadline (p_deadline) - , _registration () + : _deadline (p_deadline) + , _resource () { } template delay::delay(const duration& p_duration) - : _deadline (asyncpp::now() + p_duration) - , _registration () + : _deadline (asyncpp::now() + p_duration) + , _resource () { } inline delay::~delay() - { __impl::timer_base::unregister_resource(_registration); } + { __impl::timer_base::unregister_resource(_resource); } time_point delay::deadline() const { return _deadline; } void delay::reset(const time_point& p_deadline) { - __impl::timer_base::unregister_resource(_registration); + __impl::timer_base::unregister_resource(_resource); _deadline = p_deadline; } template void delay::reset(const duration& p_duration) { - __impl::timer_base::unregister_resource(_registration); + __impl::timer_base::unregister_resource(_resource); _deadline = asyncpp::now() + p_duration; } @@ -51,8 +51,8 @@ namespace timing { if (_deadline <= now) return result_type::ready(); - if (_registration.expired()) - _registration = timing::__impl::timer_base::register_resource(_deadline); + if (_resource.expired()) + _resource = timing::__impl::timer_base::register_resource(_deadline); return result_type::not_ready(); } diff --git a/include/asyncpp/timing/delay.pre.h b/include/asyncpp/timing/delay/delay.pre.h similarity index 100% rename from include/asyncpp/timing/delay.pre.h rename to include/asyncpp/timing/delay/delay.pre.h diff --git a/include/asyncpp/timing/impl/timer_base.h b/include/asyncpp/timing/impl/timer_base.h deleted file mode 100644 index e29fc42..0000000 --- a/include/asyncpp/timing/impl/timer_base.h +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once - -#include -#include - -#include - -#include - -#include "timer_base.pre.h" -#include "registration.h" - -namespace asyncpp { -namespace timing { -namespace __impl { - - struct timer_base - { - private: - struct registration_less_compare - { - constexpr bool operator()( - const registration_ptr_s& lhs, - const registration_ptr_s& rhs) const; - }; - - using registration_set = std::set; - - protected: - cppcore::locked _registrations; - - public: - /** - * @brief Constructor. - */ - inline ~timer_base(); - - public: - /** - * @brief Get the number of resources assigned to this timer_base. - */ - inline size_t resource_count() const; - - /** - * @brief Set the thread local current timer_base. - */ - inline void make_current(); - - /** - * @brief Set the thread local current timer_base. - */ - inline void clear_current(); - - public: - /** - * @brief Get the thread local timer_base instance. - */ - static inline timer_base* current(); - - /** - * @brief Register a new resource within this timer_base. - */ - static inline registration_ptr_w register_resource(const time_point& p_deadline); - - /** - * @brief Register a new resource within this timer_base. - */ - static inline void unregister_resource(registration_ptr_w& p_value); - - private: - /** - * @brief Create registration for a new resource. - */ - registration_ptr_w create_registration(const time_point& p_deadline); - - protected: - struct storage - { - timer_base* current { nullptr }; - }; - - /** - * @brief Get the thread local storage. - */ - static inline storage& local_storage(); - }; - -} } } diff --git a/include/asyncpp/timing/impl/timer_base.inl b/include/asyncpp/timing/impl/timer_base.inl deleted file mode 100644 index 7d31710..0000000 --- a/include/asyncpp/timing/impl/timer_base.inl +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include "timer_base.h" - -namespace asyncpp { -namespace timing { -namespace __impl { - - /* timer_base::inner_less_compare */ - - constexpr bool timer_base::registration_less_compare::operator()( - const registration_ptr_s& lhs, - const registration_ptr_s& rhs) const - { - return (lhs->deadline < rhs->deadline) - ? true - : - (lhs->deadline == rhs->deadline) - && (lhs.get() < rhs.get()) - ? true - : false; - } - - /* timer_base */ - - timer_base::~timer_base() - { - auto& s = local_storage(); - if (s.current == this) - s.current = nullptr; - } - - size_t timer_base::resource_count() const - { return _registrations.lock()->size(); } - - void timer_base::make_current() - { - auto& s = local_storage(); - if (s.current) - throw std::runtime_error("Thread local timer_base instance is already assigned!"); - s.current = this; - } - - void timer_base::clear_current() - { - auto& s = local_storage(); - if (s.current && s.current != this) - throw std::runtime_error("Thread local timer_base instance is not assigned to this instance!"); - s.current = nullptr; - } - - timer_base* timer_base::current() - { return local_storage().current; } - - registration_ptr_w timer_base::register_resource(const time_point& p_deadline) - { - auto t = current(); - - if (!t) - throw std::runtime_error("Thread local timer_base instance is not assigned!"); - - return t->create_registration(p_deadline); - } - - registration_ptr_w timer_base::create_registration(const time_point& p_deadline) - { - auto r = std::make_shared( - *this, - p_deadline, - task::current()); - - _registrations.lock()->insert(r); - - return r; - } - - void timer_base::unregister_resource(registration_ptr_w& p_value) - { - auto s = p_value.lock(); - p_value.reset(); - if (s) - s->owner._registrations.lock()->erase(s); - } - - timer_base::storage& timer_base::local_storage() - { - thread_local storage value; - return value; - } - -} } } diff --git a/include/asyncpp/timing/impl/timer_impl.h b/include/asyncpp/timing/impl/timer_impl.h deleted file mode 100644 index c0a4525..0000000 --- a/include/asyncpp/timing/impl/timer_impl.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -namespace asyncpp { -namespace timing { - - template - struct timer; - - namespace __impl - { - - /* timer_impl - default */ - - template - struct timer_impl - { - public: - using inner_type = T_inner; - using owner_type = timer; - using inner_thread_lock_type = decltype(std::declval().init_thread()); - - struct thread_lock - { - inner_thread_lock_type inner_lock; - - template - inline thread_lock(X_args&&... p_args); - - inline ~thread_lock(); - }; - - using thread_lock_ptr_u = std::unique_ptr; - - public: - owner_type& owner; - inner_type inner; - - public: - template - inline timer_impl( - owner_type& p_owner, - X_args&&... p_args); - - inline void idle( - const time_point * p_deadline); - - inline thread_lock_ptr_u init_thread(); - }; - - /* timer_impl */ - - template<> - struct timer_impl - { - public: - using owner_type = timer; - - struct thread_lock - { - inline ~thread_lock(); - }; - - using thread_lock_ptr_u = std::unique_ptr; - - public: - owner_type& owner; - - public: - inline timer_impl( - owner_type& p_owner); - - inline void idle( - const time_point * p_deadline); - - inline thread_lock_ptr_u init_thread(); - }; - - } - -} } diff --git a/include/asyncpp/timing/impl/timer_impl.inl b/include/asyncpp/timing/impl/timer_impl.inl deleted file mode 100644 index 0dce7a7..0000000 --- a/include/asyncpp/timing/impl/timer_impl.inl +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "timer_impl.h" - -namespace asyncpp { -namespace timing { -namespace __impl { - - /* timer_impl::thread_lock - default */ - - template - template - timer_impl::thread_lock::thread_lock(X_args&&... p_args) - : inner_lock(std::forward(p_args)...) - { } - - template - timer_impl::thread_lock::~thread_lock() - { owner_type::local_storage().current = nullptr; } - - /* timer_impl - default */ - - template - template - timer_impl::timer_impl( - owner_type& p_owner, - X_args&&... p_args) - : owner(p_owner) - , inner(std::forward(p_args)...) - { } - - template - void timer_impl::idle(const time_point * p_deadline) - { - { - auto r = owner._registrations.lock(); - if (!r->empty()) - { - p_deadline = merge_deadlines(p_deadline, &(*r->begin())->deadline); - } - } - - inner.idle(p_deadline); - } - - template - typename timer_impl::thread_lock_ptr_u - timer_impl::init_thread() - { return std::make_unique(inner.init_thread()); } - - - - /* timer_impl::thread_lock - default */ - - timer_impl::thread_lock::~thread_lock() - { owner_type::local_storage().current = nullptr; } - - /* timer_impl */ - - timer_impl::timer_impl( - owner_type& p_owner) - : owner(p_owner) - { } - - void timer_impl::idle( - const time_point * p_deadline) - { } - - timer_impl::thread_lock_ptr_u - timer_impl::init_thread() - { return std::unique_ptr(); } - -} } } diff --git a/include/asyncpp/timing/interval.h b/include/asyncpp/timing/interval.h index f2a055d..cde80ca 100644 --- a/include/asyncpp/timing/interval.h +++ b/include/asyncpp/timing/interval.h @@ -1,55 +1,5 @@ #pragma once -#include -#include -#include +#include "interval/interval.h" -#include "delay.h" - -namespace asyncpp { -namespace timing { - - struct interval final - : public base_stream - { - public: - using value_type = void; - using base_stream_type = base_stream; - using result_type = typename base_stream_type::result_type; - - private: - delay _delay; //!< Delay future - clock::duration _duration; //!< Interval duration. - time_point _deadline; //!< Deadline after the interval should end. - - public: - /** - * @brief Constructor. - */ - template - inline interval( - const duration& p_duration, - time_point p_deadline = time_point()); - - /** - * @brief Constructor. - */ - template - inline interval( - const time_point& p_at, - const duration& p_duration, - time_point p_deadline = time_point()); - - /** - * @brief Get the duration of the interval. - */ - inline const clock::duration& duration() const; - - public: /* stream */ - /** - * @brief Poll the result from the stream. - */ - inline result_type poll(); - }; - -} } +#include "interval/interval.inl" diff --git a/include/asyncpp/timing/interval/interval.h b/include/asyncpp/timing/interval/interval.h new file mode 100644 index 0000000..912cb0a --- /dev/null +++ b/include/asyncpp/timing/interval/interval.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include + +#include "../delay/delay.h" + +namespace asyncpp { +namespace timing { + + struct interval final + : public base_stream + { + public: + using value_type = void; + using base_stream_type = base_stream; + using result_type = typename base_stream_type::result_type; + + private: + delay _delay; //!< Delay future + clock::duration _duration; //!< Interval duration. + time_point _deadline; //!< Deadline after the interval should end. + + public: + /** + * @brief Constructor. + */ + template + inline interval( + const duration& p_duration, + time_point p_deadline = time_point()); + + /** + * @brief Constructor. + */ + template + inline interval( + const time_point& p_at, + const duration& p_duration, + time_point p_deadline = time_point()); + + /** + * @brief Get the duration of the interval. + */ + inline const clock::duration& duration() const; + + public: /* stream */ + /** + * @brief Poll the result from the stream. + */ + inline result_type poll(); + }; + +} } diff --git a/include/asyncpp/timing/interval.inl b/include/asyncpp/timing/interval/interval.inl similarity index 97% rename from include/asyncpp/timing/interval.inl rename to include/asyncpp/timing/interval/interval.inl index 8b1e17b..824c47b 100644 --- a/include/asyncpp/timing/interval.inl +++ b/include/asyncpp/timing/interval/interval.inl @@ -2,7 +2,7 @@ #include "interval.h" -#include "delay.inl" +#include "../delay/delay.inl" namespace asyncpp { namespace timing { diff --git a/include/asyncpp/timing/timeout.h b/include/asyncpp/timing/timeout.h index cda3839..6c44817 100644 --- a/include/asyncpp/timing/timeout.h +++ b/include/asyncpp/timing/timeout.h @@ -1,103 +1,9 @@ #pragma once -#include +#include "timeout/misc.h" +#include "timeout/timeout_future.h" +#include "timeout/timeout_stream.h" -#include -#include -#include - -#include "delay.h" - -namespace asyncpp { -namespace timing { - - struct timeout_exception - : public cppcore::exception - { - public: - /** - * @brief Constructor. - */ - inline timeout_exception(); - }; - - template - struct timeout; - - namespace __impl - { - - template< - typename T_derived, - typename = void> - struct timeout_impl; - - template< - typename T_inner> - struct timeout_impl< - timeout, - std::enable_if_t>>> - : public base_future< - typename std::decay_t::value_type, - timeout> - { - private: - using derived_type = timeout; - - public: - auto poll(); - }; - - template< - typename T_inner> - struct timeout_impl< - timeout, - std::enable_if_t>>> - : public base_stream< - typename std::decay_t::value_type, - timeout> - { - private: - using derived_type = timeout; - - public: - auto poll(); - }; - - } - - template - struct timeout final - : public __impl::timeout_impl> - { - public: - using inner_type = T_inner; - using value_type = typename std::decay_t::value_type; - using this_type = timeout; - using impl_type = __impl::timeout_impl; - - friend impl_type; - - private: - inner_type _inner; //!< Inner future / stream. - clock::duration _timeout; //!< Timeout. - delay _delay; //!< Delay future. - - public: - /** - * @brief Constructor. - */ - template - inline timeout( - X_inner&& p_inner, - const duration& p_timeout); - - /** - * @brief Reset the deadline. - */ - template - inline void reset( - const duration& p_timeout); - }; - -} } +#include "timeout/misc.inl" +#include "timeout/timeout_future.inl" +#include "timeout/timeout_stream.inl" diff --git a/include/asyncpp/timing/timeout.inl b/include/asyncpp/timing/timeout.inl deleted file mode 100644 index 6c79e7f..0000000 --- a/include/asyncpp/timing/timeout.inl +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include "timeout.h" - -namespace asyncpp { -namespace timing { - - namespace __impl - { - - template< - typename T_inner> - auto timeout_impl< - timeout, - std::enable_if_t>>> - ::poll() - { - auto& self = static_cast(*this); - auto r = self._inner.poll(); - - if ( r.is_not_ready() - && self._delay.poll()) - { - auto new_deadline = self._delay.deadline() + self._timeout; - self._delay.reset(new_deadline); - throw timing::timeout_exception(); - } - - return r; - } - - - template< - typename T_inner> - auto timeout_impl< - timeout, - std::enable_if_t>>> - ::poll() - { - auto& self = static_cast(*this); - auto r = self._inner.poll(); - - if (r.is_not_ready()) - { - if (self._delay.poll()) - { - self._delay.reset(self._timeout); - throw timing::timeout_exception(); - } - } - else - { - self._delay.reset(self._timeout); - } - - return r; - } - - } - - /* timeout_exception */ - - timeout_exception::timeout_exception() - : cppcore::exception::exception("timeout") - { } - - /* timer */ - - template< - typename T_inner> - template< - typename X_inner, - typename X_base, - typename X_ratio> - timeout - ::timeout( - X_inner&& p_inner, - const duration& p_duration) - : _inner (std::forward(p_inner)) - , _timeout (p_duration) - , _delay (asyncpp::now() + p_duration) - { } - - template< - typename T_inner> - template< - typename X_base, - typename X_ratio> - void timeout - ::reset(const duration& p_duration) - { - _timeout = p_duration; - _delay.reset(asyncpp::now() + p_duration); - } - -} } diff --git a/include/asyncpp/timing/timeout/misc.h b/include/asyncpp/timing/timeout/misc.h new file mode 100644 index 0000000..c1789ed --- /dev/null +++ b/include/asyncpp/timing/timeout/misc.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace asyncpp { +namespace timing { + + struct timeout_exception + : public cppcore::exception + { + public: + /** + * @brief Constructor. + */ + inline timeout_exception(); + }; + +} } diff --git a/include/asyncpp/timing/timeout/misc.inl b/include/asyncpp/timing/timeout/misc.inl new file mode 100644 index 0000000..ac690a2 --- /dev/null +++ b/include/asyncpp/timing/timeout/misc.inl @@ -0,0 +1,14 @@ +#pragma once + +#include "misc.h" + +namespace asyncpp { +namespace timing { + + /* timeout_exception */ + + timeout_exception::timeout_exception() + : cppcore::exception::exception("timeout") + { } + +} } diff --git a/include/asyncpp/timing/timeout/timeout_future.h b/include/asyncpp/timing/timeout/timeout_future.h new file mode 100644 index 0000000..bfd1184 --- /dev/null +++ b/include/asyncpp/timing/timeout/timeout_future.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include "../delay/delay.h" + +namespace asyncpp { +namespace timing { + + template + struct timeout_future final + : public base_future< + typename std::decay_t::value_type, + timeout_future> + { + public: + using inner_type = T_inner; + using value_type = typename std::decay_t::value_type; + using this_type = timeout_future; + using base_future_type = base_future; + using result_type = typename base_future_type::result_type; + + private: + inner_type _inner; //!< Inner future. + clock::duration _timeout; //!< Timeout. + delay _delay; //!< Delay future. + + public: + /** + * @brief Constructor. + */ + template + inline timeout_future( + X_inner&& p_inner, + const duration& p_timeout); + + /** + * @brief Reset the deadline. + */ + template + inline void reset( + const duration& p_timeout); + + /** + * @brief Poll the result from the future. + */ + inline result_type poll(); + }; + +} } diff --git a/include/asyncpp/timing/timeout/timeout_future.inl b/include/asyncpp/timing/timeout/timeout_future.inl new file mode 100644 index 0000000..95c16b0 --- /dev/null +++ b/include/asyncpp/timing/timeout/timeout_future.inl @@ -0,0 +1,55 @@ +#pragma once + +#include "timeout_future.h" + +namespace asyncpp { +namespace timing { + + /* timeout_future */ + + template< + typename T_inner> + template< + typename X_inner, + typename X_base, + typename X_ratio> + timeout_future::timeout_future( + X_inner&& p_inner, + const duration& p_timeout) + : _inner (std::forward(p_inner)) + , _timeout (p_timeout) + , _delay (asyncpp::now() + p_timeout) + { } + + template< + typename T_inner> + template< + typename X_base, + typename X_ratio> + void timeout_future + ::reset(const duration& p_timeout) + { + _timeout = p_timeout; + _delay.reset(_timeout); + } + + template< + typename T_inner> + typename timeout_future::result_type + timeout_future + ::poll() + { + auto r = _inner.poll(); + + if ( r.is_not_ready() + && _delay.poll()) + { + auto new_deadline = _delay.deadline() + _timeout; + _delay.reset(new_deadline); + throw timing::timeout_exception(); + } + + return r; + } + +} } diff --git a/include/asyncpp/timing/timeout/timeout_stream.h b/include/asyncpp/timing/timeout/timeout_stream.h new file mode 100644 index 0000000..7d9a391 --- /dev/null +++ b/include/asyncpp/timing/timeout/timeout_stream.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include "../delay/delay.h" + +namespace asyncpp { +namespace timing { + + template + struct timeout_stream final + : public base_stream< + typename std::decay_t::value_type, + timeout_stream> + { + public: + using inner_type = T_inner; + using value_type = typename std::decay_t::value_type; + using this_type = timeout_stream; + using base_stream_type = base_stream; + using result_type = typename base_stream_type::result_type; + + private: + inner_type _inner; //!< Inner stream. + clock::duration _timeout; //!< Timeout. + delay _delay; //!< Delay stream. + + public: + /** + * @brief Constructor. + */ + template + inline timeout_stream( + X_inner&& p_inner, + const duration& p_timeout); + + /** + * @brief Reset the deadline. + */ + template + inline void reset( + const duration& p_timeout); + + /** + * @brief Poll the result from the future. + */ + inline result_type poll(); + }; + +} } diff --git a/include/asyncpp/timing/timeout/timeout_stream.inl b/include/asyncpp/timing/timeout/timeout_stream.inl new file mode 100644 index 0000000..10cc2f4 --- /dev/null +++ b/include/asyncpp/timing/timeout/timeout_stream.inl @@ -0,0 +1,60 @@ +#pragma once + +#include "timeout_stream.h" + +namespace asyncpp { +namespace timing { + + /* timeout_stream */ + + template< + typename T_inner> + template< + typename X_inner, + typename X_base, + typename X_ratio> + timeout_stream::timeout_stream( + X_inner&& p_inner, + const duration& p_timeout) + : _inner (std::forward(p_inner)) + , _timeout (p_timeout) + , _delay (asyncpp::now() + p_timeout) + { } + + template< + typename T_inner> + template< + typename X_base, + typename X_ratio> + void timeout_stream + ::reset(const duration& p_timeout) + { + _timeout = p_timeout; + _delay.reset(_timeout); + } + + template< + typename T_inner> + typename timeout_stream::result_type + timeout_stream + ::poll() + { + auto r = _inner.poll(); + + if (r.is_not_ready()) + { + if (_delay.poll()) + { + _delay.reset(_timeout); + throw timing::timeout_exception(); + } + } + else + { + _delay.reset(_timeout); + } + + return r; + } + +} } diff --git a/include/asyncpp/timing/timer.h b/include/asyncpp/timing/timer.h index cca6831..bbd2313 100644 --- a/include/asyncpp/timing/timer.h +++ b/include/asyncpp/timing/timer.h @@ -1,50 +1,11 @@ #pragma once -#include -#include - -#include - -#include "impl/timer_base.h" -#include "impl/timer_impl.h" -#include "impl/registration.h" - -namespace asyncpp { -namespace timing { - - template - struct timer - : public __impl::timer_base - { - public: - template - friend struct __impl::timer_impl; - - using inner_type = T_inner; - using timer_impl_type = __impl::timer_impl; - - private: - timer_impl_type _impl; - - public: - /** - * @brief Constructor. - */ - template - inline timer(X_args&&... p_args); - - /** - * @brief Handle idle of the runtime. - * - * This method is called as soon as the runtime has nothing to do. - * The passed deadline is the timepoint the method should return (or null if not set). - */ - inline void idle(const time_point * p_deadline); - - /** - * @brief This method is calld by the executor when a new thread needs to be initialized. - */ - inline decltype(auto) init_thread(); - }; - -} } +#include "timer/timer.h" +#include "timer/timer_base.h" +#include "timer/timer_impl.h" +#include "timer/resource.h" + +#include "timer/timer.inl" +#include "timer/timer_base.inl" +#include "timer/timer_impl.inl" +#include "timer/resource.inl" diff --git a/include/asyncpp/timing/impl/registration.h b/include/asyncpp/timing/timer/resource.h similarity index 74% rename from include/asyncpp/timing/impl/registration.h rename to include/asyncpp/timing/timer/resource.h index 9c8155e..b767d98 100644 --- a/include/asyncpp/timing/impl/registration.h +++ b/include/asyncpp/timing/timer/resource.h @@ -11,7 +11,7 @@ namespace asyncpp { namespace timing { namespace __impl { - struct registration + struct resource { public: timer_base& owner; @@ -21,13 +21,13 @@ namespace __impl { /** * @brief Constructor. */ - inline registration( + inline resource( timer_base& p_owner, const time_point& p_deadline, const asyncpp::task& p_task); }; - using registration_ptr_w = std::weak_ptr; - using registration_ptr_s = std::shared_ptr; + using resource_ptr_w = std::weak_ptr; + using resource_ptr_s = std::shared_ptr; } } } diff --git a/include/asyncpp/timing/impl/registration.inl b/include/asyncpp/timing/timer/resource.inl similarity index 79% rename from include/asyncpp/timing/impl/registration.inl rename to include/asyncpp/timing/timer/resource.inl index b19be9b..70aafc0 100644 --- a/include/asyncpp/timing/impl/registration.inl +++ b/include/asyncpp/timing/timer/resource.inl @@ -1,14 +1,14 @@ #pragma once -#include "registration.h" +#include "resource.h" namespace asyncpp { namespace timing { namespace __impl { - /* registration */ + /* resource */ - registration::registration( + resource::resource( timer_base& p_owner, const time_point& p_deadline, const asyncpp::task& p_task) diff --git a/include/asyncpp/timing/timer/timer.h b/include/asyncpp/timing/timer/timer.h new file mode 100644 index 0000000..0dffe7c --- /dev/null +++ b/include/asyncpp/timing/timer/timer.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#include + +#include "timer_impl.h" + +namespace asyncpp { +namespace timing { + + template + struct timer + : public __impl::timer_impl + { + public: + using base_type = __impl::timer_impl; + using inner_type = T_inner; + + public: + /** + * @brief Constructor. + */ + template + inline timer(X_args&&... p_args); + + /** + * @brief Handle idle of the runtime. + * + * This method is called as soon as the runtime has nothing to do. + * The passed deadline is the timepoint the method should return (or null if not set). + */ + inline void idle(const time_point * p_deadline); + + /** + * @brief This method is calld by the executor when a new thread needs to be initialized. + * + * @return Object to hold as long as the thread is active on this timer. + */ + inline auto init_thread(); + }; + +} } diff --git a/include/asyncpp/timing/timer.inl b/include/asyncpp/timing/timer/timer.inl similarity index 60% rename from include/asyncpp/timing/timer.inl rename to include/asyncpp/timing/timer/timer.inl index 0549928..8cfb160 100644 --- a/include/asyncpp/timing/timer.inl +++ b/include/asyncpp/timing/timer/timer.inl @@ -1,11 +1,8 @@ #pragma once #include "timer.h" -#include "delay.inl" -#include "impl/timer_base.inl" -#include "impl/timer_impl.inl" -#include "impl/registration.inl" +#include "timer_impl.inl" namespace asyncpp { namespace timing { @@ -15,7 +12,7 @@ namespace timing { template template timer::timer(X_args&&... p_args) - : _impl(*this, std::forward(p_args)...) + : base_type(std::forward(p_args)...) { } template @@ -24,7 +21,7 @@ namespace timing { auto now = asyncpp::now(); { - auto r = _registrations.lock(); + auto r = this->_resources.lock(); auto it = r->begin(); while (it != r->end() && now >= (**it).deadline) { @@ -33,14 +30,11 @@ namespace timing { } } - _impl.idle(p_deadline); + base_type::idle_impl(p_deadline); } template - decltype(auto) timer::init_thread() - { - local_storage().current = this; - return _impl.init_thread(); - } + auto timer::init_thread() + { return base_type::init_thread_impl(); } } } diff --git a/include/asyncpp/timing/timer/timer_base.h b/include/asyncpp/timing/timer/timer_base.h new file mode 100644 index 0000000..cbe8e48 --- /dev/null +++ b/include/asyncpp/timing/timer/timer_base.h @@ -0,0 +1,87 @@ +#pragma once + +#include +#include + +#include + +#include + +#include "timer_base.pre.h" +#include "resource.h" + +namespace asyncpp { +namespace timing { +namespace __impl { + + struct timer_base + { + private: + struct resource_less_compare + { + constexpr bool operator()( + const resource_ptr_s& lhs, + const resource_ptr_s& rhs) const; + }; + + using resource_set = std::set; + + protected: + cppcore::locked _resources; + + public: + /** + * @brief Get the number of registered timing resources. + */ + inline size_t resource_count(); + + public: + /** + * @brief Get the thread local timer_base instance. + */ + static inline timer_base* current(); + + /** + * @brief Register a new resource within this timer_base. + */ + static inline resource_ptr_w register_resource(const time_point& p_deadline); + + /** + * @brief Register a new resource within this timer_base. + */ + static inline void unregister_resource(resource_ptr_w& p_value); + + private: + /** + * @brief Create resource for a new resource. + */ + resource_ptr_w create_resource(const time_point& p_deadline); + + public: + struct lock + { + public: + /** + * @brief Constructor. + */ + inline lock(timer_base& p_timer); + + /** + * @brief Destructor. + */ + inline ~lock(); + }; + + private: + struct storage + { + timer_base* current { nullptr }; + }; + + /** + * @brief Get the thread local storage. + */ + static inline storage& local_storage(); + }; + +} } } diff --git a/include/asyncpp/timing/timer/timer_base.inl b/include/asyncpp/timing/timer/timer_base.inl new file mode 100644 index 0000000..f50d295 --- /dev/null +++ b/include/asyncpp/timing/timer/timer_base.inl @@ -0,0 +1,83 @@ +#pragma once + +#include + +#include "timer_base.h" + +namespace asyncpp { +namespace timing { +namespace __impl { + + /* timer_base::inner_less_compare */ + + constexpr bool timer_base::resource_less_compare::operator()( + const resource_ptr_s& lhs, + const resource_ptr_s& rhs) const + { + return (lhs->deadline < rhs->deadline) + ? true + : + (lhs->deadline == rhs->deadline) + && (lhs.get() < rhs.get()) + ? true + : false; + } + + /* timer_base::lock */ + + timer_base::lock::lock(timer_base& p_timer) + { + auto& current = timer_base::local_storage().current; + if (current) + throw cppcore::invalid_operation_exception("Current timer is already assigned!"); + current = &p_timer; + } + + timer_base::lock::~lock() + { timer_base::local_storage().current = nullptr; } + + /* timer_base */ + + size_t timer_base::resource_count() + { return _resources.lock()->size(); } + + timer_base* timer_base::current() + { return local_storage().current; } + + resource_ptr_w timer_base::register_resource(const time_point& p_deadline) + { + auto t = current(); + + if (!t) + throw cppcore::invalid_operation_exception("Thread local timer_base instance is not assigned!"); + + return t->create_resource(p_deadline); + } + + void timer_base::unregister_resource(resource_ptr_w& p_value) + { + auto s = p_value.lock(); + p_value.reset(); + if (s) + s->owner._resources.lock()->erase(s); + } + + resource_ptr_w timer_base::create_resource(const time_point& p_deadline) + { + auto r = std::make_shared( + *this, + p_deadline, + task::current()); + + _resources.lock()->insert(r); + + return r; + } + + timer_base::storage& timer_base::local_storage() + { + thread_local storage value; + return value; + } + +} } } diff --git a/include/asyncpp/timing/impl/timer_base.pre.h b/include/asyncpp/timing/timer/timer_base.pre.h similarity index 100% rename from include/asyncpp/timing/impl/timer_base.pre.h rename to include/asyncpp/timing/timer/timer_base.pre.h diff --git a/include/asyncpp/timing/timer/timer_impl.h b/include/asyncpp/timing/timer/timer_impl.h new file mode 100644 index 0000000..68fe3d9 --- /dev/null +++ b/include/asyncpp/timing/timer/timer_impl.h @@ -0,0 +1,10 @@ +#pragma once + +namespace asyncpp { +namespace timing { +namespace __impl { + + template + struct timer_impl; + +} } } diff --git a/include/asyncpp/timing/timer/timer_impl.inl b/include/asyncpp/timing/timer/timer_impl.inl new file mode 100644 index 0000000..1bf4dad --- /dev/null +++ b/include/asyncpp/timing/timer/timer_impl.inl @@ -0,0 +1,76 @@ +#pragma once + +#include "timer_impl.h" +#include "timer_base.h" + +namespace asyncpp { +namespace timing { +namespace __impl { + + /* timer_impl - default */ + + template + struct timer_impl + : public timer_base + { + public: + using inner_type = T_inner; + using inner_thread_lock_type = decltype(std::declval().init_thread()); + + struct thread_lock + { + timer_base::lock timer_lock; + inner_thread_lock_type inner_lock; + + template + inline thread_lock(timer_base& p_timer, X_args&&... p_args) + : timer_lock(p_timer) + , inner_lock(std::forward(p_args)...) + { } + + inline thread_lock(thread_lock &&) = delete; + inline thread_lock(thread_lock const &) = delete; + }; + + private: + inner_type _inner; + + public: + template + inline timer_impl(X_inner&& p_inner) + : _inner(std::forward(p_inner)) + { } + + protected: + inline void idle_impl(const time_point * p_deadline) + { + { + auto r = _resources.lock(); + if (!r->empty()) + { + p_deadline = merge_deadlines(p_deadline, &(*r->begin())->deadline); + } + } + + _inner.idle(p_deadline); + } + + inline auto init_thread_impl() + { return thread_lock(*this, _inner.init_thread()); } + }; + + /* timer_impl - void */ + + template<> + struct timer_impl + : public timer_base + { + protected: + inline void idle_impl(const time_point * p_deadline) + { } + + inline auto init_thread_impl() + { return timer_base::lock(*this); } + }; + +} } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 975a22a..44aedde 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,11 +9,15 @@ Find_Package ( cppcore REQUIRED ) # Interface Library ############################################################################### +Set ( ASYNCPP_GENERATED_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated ) +Configure_File ( ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/config.h.in + ${ASYNCPP_GENERATED_INCLUDE_DIR}/asyncpp/config.h ) Set ( ASYNCPP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include ) Add_Library ( asyncpp INTERFACE ) Target_Include_Directories ( asyncpp INTERFACE $ + $ $ ) Target_Link_Libraries ( asyncpp INTERFACE @@ -31,6 +35,8 @@ If ( ASYNCPP_INSTALL_HEADER ) DESTINATION ${ASYNCPP_INSTALL_DIR_INCLUDE} ) Install ( DIRECTORY ${ASYNCPP_INCLUDE_DIR}/asyncpp DESTINATION ${ASYNCPP_INSTALL_DIR_INCLUDE} ) + Install ( DIRECTORY ${ASYNCPP_GENERATED_INCLUDE_DIR}/cppcore + DESTINATION ${ASYNCPP_INSTALL_DIR_INCLUDE} ) Install ( TARGETS asyncpp EXPORT asyncpp DESTINATION ${ASYNCPP_INSTALL_DIR_INCLUDE} ) diff --git a/test/asyncpp/executor/current_thread_tests.cpp b/test/asyncpp/executor/current_thread_tests.cpp index 9f2027b..38562e9 100644 --- a/test/asyncpp/executor/current_thread_tests.cpp +++ b/test/asyncpp/executor/current_thread_tests.cpp @@ -3,9 +3,8 @@ #include "../../helper/now_mock.h" #include "../../helper/runtime_mock.h" -#include -#include #include +#include using namespace ::testing; using namespace ::asyncpp; diff --git a/test/asyncpp/timing/delay_tests.cpp b/test/asyncpp/timing/delay_tests.cpp index 40cd8d3..3626f67 100644 --- a/test/asyncpp/timing/delay_tests.cpp +++ b/test/asyncpp/timing/delay_tests.cpp @@ -3,7 +3,6 @@ #include "../../helper/now_mock.h" #include -#include using namespace ::testing; using namespace ::asyncpp; @@ -19,7 +18,7 @@ TEST(delay_tests, poll) .WillOnce(Return(time_point(std::chrono::seconds(1000)))); asyncpp::timing::timer t; - t.make_current(); + timing::__impl::timer_base::lock l(t); { delay f(time_point(std::chrono::seconds(1000))); diff --git a/test/asyncpp/timing/interval_tests.cpp b/test/asyncpp/timing/interval_tests.cpp index e86feeb..0d53df6 100644 --- a/test/asyncpp/timing/interval_tests.cpp +++ b/test/asyncpp/timing/interval_tests.cpp @@ -3,7 +3,6 @@ #include "../../helper/now_mock.h" #include -#include using namespace ::testing; using namespace ::asyncpp; @@ -23,7 +22,7 @@ TEST(interval_tests, poll) // 20 / 5 = 4 // 4 x Ready - t.make_current(); + timing::__impl::timer_base::lock l(t); EXPECT_CALL(m, now) .WillOnce(Return(time_point(std::chrono::seconds(0)))); diff --git a/test/asyncpp/timing/timeout_tests.cpp b/test/asyncpp/timing/timeout_tests.cpp index c8d9792..5b45971 100644 --- a/test/asyncpp/timing/timeout_tests.cpp +++ b/test/asyncpp/timing/timeout_tests.cpp @@ -2,7 +2,6 @@ #include "../../helper/now_mock.h" -#include #include using namespace ::testing; @@ -68,8 +67,8 @@ TEST(timeout_tests, poll_future_no_timeout) InSequence seq; StrictMock m; - asyncpp::timing::timer tmr; - tmr.make_current(); + timing::timer tmr; + timing::__impl::timer_base::lock l(tmr); EXPECT_CALL(m, now()) .WillOnce(Return(time_point(std::chrono::seconds(0)))); @@ -101,8 +100,8 @@ TEST(timeout_tests, poll_future_timeout) InSequence seq; StrictMock m; - asyncpp::timing::timer tmr; - tmr.make_current(); + timing::timer tmr; + timing::__impl::timer_base::lock l(tmr); EXPECT_CALL(m, now()) .WillOnce(Return(time_point(std::chrono::seconds(0)))); @@ -133,8 +132,8 @@ TEST(timeout_tests, poll_stream_no_timeout) InSequence seq; StrictMock m; - asyncpp::timing::timer tmr; - tmr.make_current(); + timing::timer tmr; + timing::__impl::timer_base::lock l(tmr); EXPECT_CALL(m, now()) .WillOnce(Return(time_point(std::chrono::seconds(0)))); @@ -183,8 +182,8 @@ TEST(timeout_tests, poll_stream_timeout) InSequence seq; StrictMock m; - asyncpp::timing::timer tmr; - tmr.make_current(); + timing::timer tmr; + timing::__impl::timer_base::lock l(tmr); EXPECT_CALL(m, now()) .WillOnce(Return(time_point(std::chrono::seconds(0)))); diff --git a/test/asyncpp/timing/timer_tests.cpp b/test/asyncpp/timing/timer_tests.cpp index 9189641..1b10d1c 100644 --- a/test/asyncpp/timing/timer_tests.cpp +++ b/test/asyncpp/timing/timer_tests.cpp @@ -4,27 +4,18 @@ #include "../../helper/runtime_mock.h" #include -#include using namespace ::testing; using namespace ::asyncpp; -TEST(timer_tests, current) +TEST(timer_tests, lock) { - { timing::timer t; EXPECT_EQ(nullptr, timing::__impl::timer_base::current()); - t.make_current(); - - EXPECT_EQ(&t, timing::__impl::timer_base::current()); - - t.clear_current(); - - EXPECT_EQ(nullptr, timing::__impl::timer_base::current()); - - t.make_current(); + { + timing::__impl::timer_base::lock l(t); EXPECT_EQ(&t, timing::__impl::timer_base::current()); } @@ -39,7 +30,7 @@ TEST(timer_tests, resource_registration) .WillRepeatedly(Return(time_point(std::chrono::seconds(0)))); timing::timer t; - t.make_current(); + timing::__impl::timer_base::lock l(t); auto f1 = std::make_unique(std::chrono::seconds(10)); auto f2 = std::make_unique(std::chrono::seconds(20)); @@ -83,7 +74,7 @@ TEST(timer_tests, idle) StrictMock nm; StrictMock rm; timing::timer&> t(rm); - t.make_current(); + timing::__impl::timer_base::lock l(t); EXPECT_CALL(nm, now); EXPECT_CALL(rm, idle(nullptr));