| @@ -8,7 +8,7 @@ | |||||
| "name": "future tests", | "name": "future tests", | ||||
| "type": "cppdbg", | "type": "cppdbg", | ||||
| "request": "launch", | "request": "launch", | ||||
| "program": "${workspaceFolder}/build/test/test-asyncpp-future-tests", | |||||
| "program": "${workspaceFolder}/build/test/test-asyncpp-timer-delay-tests", | |||||
| "args": [], | "args": [], | ||||
| "stopAtEntry": false, | "stopAtEntry": false, | ||||
| "cwd": "${workspaceFolder}", | "cwd": "${workspaceFolder}", | ||||
| @@ -1,4 +1,8 @@ | |||||
| { | { | ||||
| "files.trimTrailingWhitespace": true, | |||||
| "files.trimFinalNewlines": true, | |||||
| "files.insertFinalNewline": true, | |||||
| "files.eol": "\n", | |||||
| "files.associations": { | "files.associations": { | ||||
| "optional": "cpp", | "optional": "cpp", | ||||
| "memory": "cpp", | "memory": "cpp", | ||||
| @@ -48,6 +52,7 @@ | |||||
| "memory_resource": "cpp", | "memory_resource": "cpp", | ||||
| "random": "cpp", | "random": "cpp", | ||||
| "set": "cpp", | "set": "cpp", | ||||
| "string": "cpp" | |||||
| "string": "cpp", | |||||
| "chrono": "cpp" | |||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -1 +1 @@ | |||||
| Subproject commit 94b9877d65e46c9d8169ebc46f163d02e4d9dcf3 | |||||
| Subproject commit 165bf7c7702ec184b46b046889b62ef4a63afccf | |||||
| @@ -1,9 +1,3 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <asyncpp/future.h> | |||||
| #include <asyncpp/result.h> | |||||
| #include <asyncpp/stream.h> | |||||
| #include <asyncpp/future.inl> | |||||
| #include <asyncpp/result.inl> | |||||
| #include <asyncpp/stream.inl> | |||||
| #include <asyncpp/core.h> | |||||
| @@ -0,0 +1,9 @@ | |||||
| #pragma once | |||||
| #include "core/future.h" | |||||
| #include "core/result.h" | |||||
| #include "core/stream.h" | |||||
| #include "core/future.inl" | |||||
| #include "core/result.inl" | |||||
| #include "core/stream.inl" | |||||
| @@ -17,12 +17,11 @@ namespace asyncpp | |||||
| using inner_value_type = typename future_type::value_type; | using inner_value_type = typename future_type::value_type; | ||||
| using lambda_type = T_lambda; | using lambda_type = T_lambda; | ||||
| using value_type = decltype(std::declval<lambda_type>()(std::declval<inner_value_type>())); | using value_type = decltype(std::declval<lambda_type>()(std::declval<inner_value_type>())); | ||||
| using result_type = future_result<value_type>; | |||||
| template<typename X_future> | template<typename X_future> | ||||
| static inline auto poll(X_future& self) | static inline auto poll(X_future& self) | ||||
| { | { | ||||
| using result_type = future_result<value_type>; | |||||
| auto r = self.ref.future.poll(); | auto r = self.ref.future.poll(); | ||||
| return r | return r | ||||
| ? result_type::ready(self.ref.lambda(*r)) | ? result_type::ready(self.ref.lambda(*r)) | ||||
| @@ -2,17 +2,11 @@ | |||||
| #include <variant> | #include <variant> | ||||
| #include "result.pre.h" | |||||
| namespace asyncpp | namespace asyncpp | ||||
| { | { | ||||
| enum class result_status | |||||
| { | |||||
| unknown = 0, | |||||
| not_ready, | |||||
| ready, | |||||
| done, | |||||
| }; | |||||
| namespace __impl | namespace __impl | ||||
| { | { | ||||
| @@ -127,10 +121,4 @@ namespace asyncpp | |||||
| } | } | ||||
| template<typename T_value> | |||||
| using future_result = __impl::result<false, T_value>; | |||||
| template<typename T_value> | |||||
| using stream_result = __impl::result<true, T_value>; | |||||
| } | } | ||||
| @@ -0,0 +1,30 @@ | |||||
| #pragma once | |||||
| #include <variant> | |||||
| namespace asyncpp | |||||
| { | |||||
| enum class result_status | |||||
| { | |||||
| unknown = 0, | |||||
| not_ready, | |||||
| ready, | |||||
| done, | |||||
| }; | |||||
| namespace __impl | |||||
| { | |||||
| template<bool for_stream, typename T_value> | |||||
| struct result; | |||||
| } | |||||
| template<typename T_value> | |||||
| using future_result = __impl::result<false, T_value>; | |||||
| template<typename T_value> | |||||
| using stream_result = __impl::result<true, T_value>; | |||||
| } | |||||
| @@ -0,0 +1,7 @@ | |||||
| #pragma once | |||||
| #include "timer/delay.h" | |||||
| #include "timer/misc.h" | |||||
| #include "timer/delay.inl" | |||||
| #include "timer/misc.inl" | |||||
| @@ -0,0 +1,52 @@ | |||||
| #pragma once | |||||
| #include "misc.h" | |||||
| #include "timer.h" | |||||
| namespace asyncpp { | |||||
| namespace timer { | |||||
| struct delay | |||||
| { | |||||
| public: | |||||
| friend struct future_trait<delay, void>; | |||||
| private: | |||||
| time_point _timeout; | |||||
| registration _registration; | |||||
| public: | |||||
| /** | |||||
| * @brief Constructor. Create a delay at the given timeout. | |||||
| */ | |||||
| inline delay(const time_point& p_timeout); | |||||
| /** | |||||
| * @brief Constructor. Create a delay with the given duration. | |||||
| */ | |||||
| template<typename T_base, typename T_ratio> | |||||
| inline delay(const duration<T_base, T_ratio>& p_duration); | |||||
| /** | |||||
| * @brief Destructor. | |||||
| */ | |||||
| inline ~delay(); | |||||
| /** | |||||
| * @brief Get the current timeout of the delay. | |||||
| */ | |||||
| inline time_point timeout() const; | |||||
| /** | |||||
| * @brief Reset the delay to a new time point. | |||||
| */ | |||||
| inline void reset(const time_point& p_timeout); | |||||
| /** | |||||
| * @brief Reset the delay to the given duration. | |||||
| */ | |||||
| template<typename T_base, typename T_ratio> | |||||
| inline void reset(const duration<T_base, T_ratio>& p_duration); | |||||
| }; | |||||
| } } | |||||
| @@ -0,0 +1,72 @@ | |||||
| #pragma once | |||||
| #include <asyncpp/core/future.pre.h> | |||||
| #include <asyncpp/core/result.pre.h> | |||||
| #include "delay.h" | |||||
| #include "timer.inl" | |||||
| namespace asyncpp { | |||||
| namespace timer { | |||||
| /* delay */ | |||||
| delay::delay(const time_point& p_timeout) | |||||
| : _timeout (p_timeout) | |||||
| , _registration (*this) | |||||
| { } | |||||
| template<typename T_base, typename T_ratio> | |||||
| delay::delay(const duration<T_base, T_ratio>& p_duration) | |||||
| : _timeout(clock::now() + p_duration) | |||||
| { } | |||||
| inline delay::~delay() | |||||
| { timer::unregister_resource(_registration); } | |||||
| time_point delay::timeout() const | |||||
| { return _timeout; } | |||||
| void delay::reset(const time_point& p_timeout) | |||||
| { | |||||
| timer::unregister_resource(_registration); | |||||
| _timeout = p_timeout; | |||||
| } | |||||
| template<typename T_base, typename T_ratio> | |||||
| void delay::reset(const duration<T_base, T_ratio>& p_duration) | |||||
| { | |||||
| timer::unregister_resource(_registration); | |||||
| _timeout = now() + p_duration; | |||||
| } | |||||
| } } | |||||
| namespace asyncpp | |||||
| { | |||||
| /* future_impl for timer::delay */ | |||||
| template<> | |||||
| struct future_trait<timer::delay, void> | |||||
| : public future_base<future<timer::delay, void>> | |||||
| { | |||||
| using value_type = void; | |||||
| using result_type = future_result<value_type>; | |||||
| template<typename X_future> | |||||
| static inline auto poll(X_future& self) | |||||
| { | |||||
| auto now = timer::now(); | |||||
| if (self.ref._timeout <= now) | |||||
| return result_type::ready(); | |||||
| timer::timer::register_resource(self.ref._registration); | |||||
| return result_type::not_ready(); | |||||
| } | |||||
| }; | |||||
| } | |||||
| @@ -0,0 +1,20 @@ | |||||
| #pragma once | |||||
| #include <chrono> | |||||
| namespace asyncpp { | |||||
| namespace timer { | |||||
| using clock = std::chrono::steady_clock; | |||||
| using time_point = clock::time_point; | |||||
| template<typename T_base, typename T_ratio> | |||||
| using duration = std::chrono::duration<T_base ,T_ratio>; | |||||
| /** | |||||
| * @brief Get the current time point. | |||||
| */ | |||||
| inline time_point now(); | |||||
| } } | |||||
| @@ -0,0 +1,13 @@ | |||||
| #pragma once | |||||
| #include "misc.h" | |||||
| namespace asyncpp { | |||||
| namespace timer { | |||||
| #ifndef __asyncpp_has_impl_timer_now | |||||
| time_point now() | |||||
| { return clock::now(); } | |||||
| #endif | |||||
| } } | |||||
| @@ -0,0 +1,103 @@ | |||||
| #pragma once | |||||
| #include <set> | |||||
| #include <memory> | |||||
| #include "misc.h" | |||||
| namespace asyncpp { | |||||
| namespace timer { | |||||
| struct delay; | |||||
| struct timer; | |||||
| struct registration | |||||
| { | |||||
| public: | |||||
| struct inner | |||||
| { | |||||
| timer& owner; | |||||
| time_point timeout; | |||||
| /** | |||||
| * @brief Constructor. | |||||
| */ | |||||
| inline inner(timer& p_owner, time_point p_timeout); | |||||
| }; | |||||
| using inner_ptr_w = std::weak_ptr<inner>; | |||||
| public: | |||||
| delay& owner; | |||||
| inner_ptr_w ptr; | |||||
| /** | |||||
| * @brief Constructor. | |||||
| */ | |||||
| inline registration(delay& p_owner); | |||||
| }; | |||||
| struct timer | |||||
| { | |||||
| private: | |||||
| using inner_ptr_s = std::shared_ptr<registration::inner>; | |||||
| struct inner_less_compare | |||||
| { constexpr bool operator()(const inner_ptr_s& lhs, const inner_ptr_s& rhs) const; }; | |||||
| using inner_set = std::set<inner_ptr_s, inner_less_compare>; | |||||
| private: | |||||
| inner_set _registrations; | |||||
| public: | |||||
| /** | |||||
| * @brief Get the number of resources assigned to this timer. | |||||
| */ | |||||
| inline size_t resource_count() const; | |||||
| /** | |||||
| * @brief Set the thread local current timer. | |||||
| */ | |||||
| inline void make_current(); | |||||
| /** | |||||
| * @brief Set the thread local current timer. | |||||
| */ | |||||
| inline void clear_current(); | |||||
| public: | |||||
| /** | |||||
| * @brief Get the thread local timer instance. | |||||
| */ | |||||
| static inline timer* current(); | |||||
| /** | |||||
| * @brief Register a new resource within this timer. | |||||
| */ | |||||
| static inline void register_resource(registration& p_value); | |||||
| /** | |||||
| * @brief Register a new resource within this timer. | |||||
| */ | |||||
| static inline void unregister_resource(registration& p_value); | |||||
| private: | |||||
| /** | |||||
| * @brief Add a new resource to the timer. | |||||
| */ | |||||
| inline inner_ptr_s make_inner(registration& p_value); | |||||
| private: | |||||
| struct storage | |||||
| { | |||||
| timer* current { nullptr }; | |||||
| }; | |||||
| /** | |||||
| * @brief Get the thread local storage. | |||||
| */ | |||||
| static inline storage& local_storage(); | |||||
| }; | |||||
| } } | |||||
| @@ -0,0 +1,102 @@ | |||||
| #pragma once | |||||
| #include "timer.h" | |||||
| #include "delay.inl" | |||||
| namespace asyncpp { | |||||
| namespace timer { | |||||
| /* registration::inner */ | |||||
| registration::inner::inner(timer& p_owner, time_point p_timeout) | |||||
| : owner (p_owner) | |||||
| , timeout (p_timeout) | |||||
| { } | |||||
| /* registration */ | |||||
| registration::registration(delay& p_owner) | |||||
| : owner(p_owner) | |||||
| , ptr () | |||||
| { } | |||||
| /* timer::inner_less_compare */ | |||||
| constexpr bool timer::inner_less_compare::operator()(const inner_ptr_s& lhs, const inner_ptr_s& rhs) const | |||||
| { | |||||
| return (lhs->timeout < rhs->timeout) | |||||
| ? true | |||||
| : | |||||
| (lhs->timeout == rhs->timeout) | |||||
| && (lhs.get() < rhs.get()) | |||||
| ? true | |||||
| : false; | |||||
| } | |||||
| /* timer */ | |||||
| size_t timer::resource_count() const | |||||
| { return _registrations.size(); } | |||||
| void timer::make_current() | |||||
| { | |||||
| auto& s = local_storage(); | |||||
| if (s.current) | |||||
| throw std::runtime_error("Thread local timer instance is already assigned!"); | |||||
| s.current = this; | |||||
| } | |||||
| void timer::clear_current() | |||||
| { | |||||
| auto& s = local_storage(); | |||||
| if (s.current && s.current != this) | |||||
| throw std::runtime_error("Thread local timer instance is not assigned to this instance!"); | |||||
| s.current = nullptr; | |||||
| } | |||||
| timer* timer::current() | |||||
| { return local_storage().current; } | |||||
| void timer::register_resource(registration& p_value) | |||||
| { | |||||
| auto s = p_value.ptr.lock(); | |||||
| if (s && s->timeout != p_value.owner.timeout()) | |||||
| { | |||||
| unregister_resource(p_value); | |||||
| s.reset(); | |||||
| } | |||||
| if (!s) | |||||
| { | |||||
| auto t = current(); | |||||
| if (!t) | |||||
| throw std::runtime_error("Thread local timer instance is not assigned!"); | |||||
| p_value.ptr = t->make_inner(p_value); | |||||
| } | |||||
| } | |||||
| void timer::unregister_resource(registration& p_value) | |||||
| { | |||||
| auto s = p_value.ptr.lock(); | |||||
| p_value.ptr.reset(); | |||||
| if (s) | |||||
| s->owner._registrations.erase(s); | |||||
| } | |||||
| inline timer::inner_ptr_s timer::make_inner(registration& p_value) | |||||
| { | |||||
| auto s = std::make_shared<registration::inner>(*this, p_value.owner.timeout()); | |||||
| _registrations.insert(s); | |||||
| return s; | |||||
| } | |||||
| timer::storage& timer::local_storage() | |||||
| { | |||||
| thread_local storage value; | |||||
| return value; | |||||
| } | |||||
| } } | |||||
| @@ -38,7 +38,7 @@ ForEach ( FILE IN LISTS ASYNCPP_TEST_SOURCE_FILES ) | |||||
| Target_Link_Libraries ( ${TEST_NAME} | Target_Link_Libraries ( ${TEST_NAME} | ||||
| PUBLIC | PUBLIC | ||||
| asyncpp | asyncpp | ||||
| GTest::Main ) | |||||
| GMock::Main ) | |||||
| # Sanitizers | # Sanitizers | ||||
| If ( Sanitizers_FOUND ) | If ( Sanitizers_FOUND ) | ||||
| @@ -0,0 +1,45 @@ | |||||
| #include <gtest/gtest.h> | |||||
| #include "../../helper/now_mock.h" | |||||
| #include <asyncpp.h> | |||||
| #include <asyncpp/timer.h> | |||||
| using namespace ::testing; | |||||
| using namespace ::asyncpp; | |||||
| using namespace ::asyncpp::timer; | |||||
| TEST(delay_tests, poll) | |||||
| { | |||||
| StrictMock<now_mock> m; | |||||
| EXPECT_CALL(m, now) | |||||
| .WillOnce(Return(time_point(std::chrono::seconds( 0)))) | |||||
| .WillOnce(Return(time_point(std::chrono::seconds( 999)))) | |||||
| .WillOnce(Return(time_point(std::chrono::seconds(1000)))); | |||||
| asyncpp::timer::timer t; | |||||
| t.make_current(); | |||||
| { | |||||
| delay d(time_point(std::chrono::seconds(1000))); | |||||
| auto f = as_future(d); | |||||
| EXPECT_EQ(0, t.resource_count()); | |||||
| auto r0 = f.poll(); | |||||
| ASSERT_FALSE(r0); | |||||
| EXPECT_EQ(1, t.resource_count()); | |||||
| auto r1 = f.poll(); | |||||
| ASSERT_FALSE(r1); | |||||
| EXPECT_EQ(1, t.resource_count()); | |||||
| auto r2 = f.poll(); | |||||
| ASSERT_TRUE (r2); | |||||
| } | |||||
| EXPECT_EQ(0, t.resource_count()); | |||||
| } | |||||
| @@ -0,0 +1,40 @@ | |||||
| #pragma once | |||||
| #include <gtest/gtest.h> | |||||
| #include <gmock/gmock.h> | |||||
| #define __asyncpp_has_impl_timer_now | |||||
| #include <asyncpp/timer/misc.h> | |||||
| struct now_mock; | |||||
| static now_mock * __now_mock_instance; | |||||
| struct now_mock | |||||
| { | |||||
| public: | |||||
| inline now_mock() | |||||
| { __now_mock_instance = this; } | |||||
| inline ~now_mock() | |||||
| { | |||||
| if (__now_mock_instance == this) | |||||
| __now_mock_instance = nullptr; | |||||
| } | |||||
| public: | |||||
| MOCK_METHOD0(now, asyncpp::timer::time_point()); | |||||
| }; | |||||
| namespace asyncpp { | |||||
| namespace timer { | |||||
| time_point now() | |||||
| { | |||||
| return __now_mock_instance | |||||
| ? __now_mock_instance->now() | |||||
| : std::chrono::steady_clock::now(); | |||||
| } | |||||
| } } | |||||