From 1b2f16a6cf58baa06747832f56c30a2505d9d3e3 Mon Sep 17 00:00:00 2001 From: bergmann Date: Sat, 23 Nov 2019 03:57:46 +0100 Subject: [PATCH] * Implemented test for 'interval' with 'current_thread' executor * Refactored 'timer' class * Fixed bugs in 'interval' --- include/asyncpp/core/future/and_then.inl | 2 +- include/asyncpp/core/future/map.inl | 2 +- include/asyncpp/core/result.h | 12 +++ include/asyncpp/core/result.inl | 16 +++ include/asyncpp/core/stream/for_each.h | 3 +- include/asyncpp/core/stream/for_each.inl | 2 +- include/asyncpp/executor/current_thread.inl | 5 +- include/asyncpp/timing/impl/timer_base.h | 2 +- include/asyncpp/timing/impl/timer_impl.h | 80 ++++++++++++++ include/asyncpp/timing/impl/timer_impl.inl | 73 +++++++++++++ include/asyncpp/timing/interval.inl | 9 +- include/asyncpp/timing/timer.h | 42 ++------ include/asyncpp/timing/timer.inl | 47 ++------ .../asyncpp/executor/current_thread_tests.cpp | 100 +++++++++++++++++- test/asyncpp/timing/interval_tests.cpp | 19 ++-- test/helper/runtime_mock.h | 6 +- 16 files changed, 326 insertions(+), 94 deletions(-) create mode 100644 include/asyncpp/timing/impl/timer_impl.h create mode 100644 include/asyncpp/timing/impl/timer_impl.inl diff --git a/include/asyncpp/core/future/and_then.inl b/include/asyncpp/core/future/and_then.inl index 997133f..81f3c26 100644 --- a/include/asyncpp/core/future/and_then.inl +++ b/include/asyncpp/core/future/and_then.inl @@ -48,7 +48,7 @@ namespace asyncpp if (!r) return result_type::not_ready(); - self->second = std::make_unique(as_future(self->lambda(*r))); + self->second = std::make_unique(as_future(r.call(self->lambda))); } } } diff --git a/include/asyncpp/core/future/map.inl b/include/asyncpp/core/future/map.inl index 8f32a52..34074e8 100644 --- a/include/asyncpp/core/future/map.inl +++ b/include/asyncpp/core/future/map.inl @@ -38,7 +38,7 @@ namespace asyncpp { auto r = self->future.poll(); return r - ? result_type::ready(self->lambda(*r)) + ? result_type::ready(r.call(self->lambda)) : result_type::not_ready(); } diff --git a/include/asyncpp/core/result.h b/include/asyncpp/core/result.h index 6c2ce10..330aec2 100644 --- a/include/asyncpp/core/result.h +++ b/include/asyncpp/core/result.h @@ -111,6 +111,18 @@ namespace asyncpp */ inline const_reference_type value() const; + /** + * @brief Call the passed closure with the value stored in the result. + */ + template + inline auto call(const X_lambda& p_lambda); + + /** + * @brief Call the passed closure with the value stored in the result. + */ + template + inline auto call(const X_lambda& p_lambda) const; + public: inline operator bool() const; inline pointer_type operator-> (); diff --git a/include/asyncpp/core/result.inl b/include/asyncpp/core/result.inl index cd7855e..080f1e8 100644 --- a/include/asyncpp/core/result.inl +++ b/include/asyncpp/core/result.inl @@ -91,6 +91,18 @@ namespace asyncpp ::value() const { return std::get(_storage).value; } + template + template + auto result + ::call(const X_lambda& p_lambda) + { return p_lambda(value()); } + + template + template + auto result + ::call(const X_lambda& p_lambda) const + { return p_lambda(value()); } + template result ::operator bool() const @@ -181,6 +193,10 @@ namespace asyncpp inline void value() { throw std::runtime_error("'void' result does not store any value!"); } + template + inline auto call(const X_lambda& p_lambda) + { return p_lambda(); } + inline operator bool() const { return _status == result_status::ready; } diff --git a/include/asyncpp/core/stream/for_each.h b/include/asyncpp/core/stream/for_each.h index 81a490f..a4e9050 100644 --- a/include/asyncpp/core/stream/for_each.h +++ b/include/asyncpp/core/stream/for_each.h @@ -45,8 +45,9 @@ namespace asyncpp { using stream_type = T_stream; using inner_value_type = typename stream_type::value_type; + using inner_result_type = future_result; using lambda_type = T_lambda; - using value_type = decltype(std::declval()(std::declval())); + using value_type = decltype(std::declval().call(std::declval())); using result_type = future_result; template diff --git a/include/asyncpp/core/stream/for_each.inl b/include/asyncpp/core/stream/for_each.inl index 004d72d..2d2dfc7 100644 --- a/include/asyncpp/core/stream/for_each.inl +++ b/include/asyncpp/core/stream/for_each.inl @@ -45,7 +45,7 @@ namespace asyncpp if (r.is_not_ready()) return result_type::not_ready(); - self->lambda(*r); + r.call(self->lambda); } } diff --git a/include/asyncpp/executor/current_thread.inl b/include/asyncpp/executor/current_thread.inl index 73a2c37..d9e30ed 100644 --- a/include/asyncpp/executor/current_thread.inl +++ b/include/asyncpp/executor/current_thread.inl @@ -51,7 +51,10 @@ namespace executor { void current_thread ::run(X_future&& p_future) { - __impl::current_executor_lock l(executor::local_storage(), *this); + auto executor_lock = __impl::current_executor_lock(executor::local_storage(), *this); + auto runtime_lock = _runtime.init_thread(); + + (void)runtime_lock; spawn(std::forward(p_future)); diff --git a/include/asyncpp/timing/impl/timer_base.h b/include/asyncpp/timing/impl/timer_base.h index cf81212..e29fc42 100644 --- a/include/asyncpp/timing/impl/timer_base.h +++ b/include/asyncpp/timing/impl/timer_base.h @@ -73,7 +73,7 @@ namespace __impl { */ registration_ptr_w create_registration(const time_point& p_deadline); - private: + protected: struct storage { timer_base* current { nullptr }; diff --git a/include/asyncpp/timing/impl/timer_impl.h b/include/asyncpp/timing/impl/timer_impl.h new file mode 100644 index 0000000..91dbe92 --- /dev/null +++ b/include/asyncpp/timing/impl/timer_impl.h @@ -0,0 +1,80 @@ +#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 asyncpp::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 asyncpp::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 new file mode 100644 index 0000000..da1f05a --- /dev/null +++ b/include/asyncpp/timing/impl/timer_impl.inl @@ -0,0 +1,73 @@ +#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 asyncpp::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 asyncpp::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.inl b/include/asyncpp/timing/interval.inl index 8e57125..4f26dac 100644 --- a/include/asyncpp/timing/interval.inl +++ b/include/asyncpp/timing/interval.inl @@ -42,16 +42,17 @@ namespace asyncpp stream_trait ::poll(X_stream& self) { + if ( self->_deadline.time_since_epoch().count() + && self->_delay->deadline() >= self->_deadline + && asyncpp::now() >= self->_delay->deadline()) + return result_type::done(); + auto ret = self->_delay.poll(); if (ret.is_not_ready()) return result_type::not_ready(); auto now = self->_delay->deadline(); auto new_deadline = now + self->_duration; - if ( self->_deadline.time_since_epoch().count() - && new_deadline >= self->_deadline) - return result_type::done(); - self->_delay->reset(new_deadline); return result_type::ready(); diff --git a/include/asyncpp/timing/timer.h b/include/asyncpp/timing/timer.h index c77c416..de16d55 100644 --- a/include/asyncpp/timing/timer.h +++ b/include/asyncpp/timing/timer.h @@ -6,37 +6,25 @@ #include #include "impl/timer_base.h" +#include "impl/timer_impl.h" #include "impl/registration.h" namespace asyncpp { namespace timing { - namespace __impl - { - - template - struct storage - { - using inner_type = T_inner; - - inner_type inner; - - template - inline storage(X_args&&... p_args); - }; - - } - - template + template struct timer : public __impl::timer_base { public: - using inner_type = T_inner; - using storage_type = __impl::storage; + template + friend struct __impl::timer_impl; + + using inner_type = T_inner; + using timer_impl_type = __impl::timer_impl; private: - storage_type _storage; + timer_impl_type _impl; public: /** @@ -53,20 +41,10 @@ namespace timing { */ inline void idle(const asyncpp::time_point * p_deadline); - private: - /** - * @brief Call the idle method of the inner runtime. - */ - template - inline auto inner_idle(const asyncpp::time_point * p_deadline) - -> std::enable_if_t>; - /** - * @brief Call the idle method of the inner runtime. + * @brief This method is calld by the executor when a new thread needs to be initialized. */ - template - inline auto inner_idle(const asyncpp::time_point * p_deadline) - -> std::enable_if_t>; + inline decltype(auto) init_thread(); }; } } diff --git a/include/asyncpp/timing/timer.inl b/include/asyncpp/timing/timer.inl index 5ec0903..b35973f 100644 --- a/include/asyncpp/timing/timer.inl +++ b/include/asyncpp/timing/timer.inl @@ -6,36 +6,18 @@ #include "delay.inl" #include "impl/timer_base.inl" +#include "impl/timer_impl.inl" #include "impl/registration.inl" namespace asyncpp { namespace timing { - namespace __impl - { - - template - template - storage::storage(X_args&&... p_args) - : inner(std::forward(p_args)...) - { } - - template<> - struct storage - { - using inner_type = void; - - inline storage() = default; - }; - - } - /* timer */ template template timer::timer(X_args&&... p_args) - : _storage(std::forward(p_args)...) + : _impl(*this, std::forward(p_args)...) { } template @@ -56,31 +38,14 @@ namespace timing { } } - inner_idle(p_deadline); - } - - template - template - auto timer::inner_idle(const asyncpp::time_point * p_deadline) - -> std::enable_if_t> - { - /* no-op */ + _impl.idle(p_deadline); } template - template - auto timer::inner_idle(const asyncpp::time_point * p_deadline) - -> std::enable_if_t> + decltype(auto) timer::init_thread() { - { - auto r = _registrations.lock(); - if (!r->empty()) - { - p_deadline = merge_deadlines(p_deadline, &(*r->begin())->deadline); - } - } - - _storage.inner.idle(p_deadline); + local_storage().current = this; + return _impl.init_thread(); } } } diff --git a/test/asyncpp/executor/current_thread_tests.cpp b/test/asyncpp/executor/current_thread_tests.cpp index 5972bdf..6d66761 100644 --- a/test/asyncpp/executor/current_thread_tests.cpp +++ b/test/asyncpp/executor/current_thread_tests.cpp @@ -1,9 +1,11 @@ #include +#include "../../helper/now_mock.h" #include "../../helper/runtime_mock.h" -#include +#include #include +#include using namespace ::testing; using namespace ::asyncpp; @@ -14,6 +16,11 @@ struct test task_ptr_w task; }; +struct mock +{ + MOCK_METHOD0(call, void()); +}; + namespace asyncpp { @@ -37,11 +44,13 @@ namespace asyncpp } TEST(current_thread_tests, run) -{ +{/* Sequence s; StrictMock m; executor::current_thread&> e(m); + EXPECT_CALL(m, init_thread()); + test t; auto f = as_future(t); @@ -59,5 +68,90 @@ TEST(current_thread_tests, run) s->notify(); }));; - e.run(f); + e.run(f); */ +} + +TEST(current_thread_tests, interval) +{ + using mock_type = StrictMock; + using now_mock_type = StrictMock; + using runtime_mock_type = StrictMock; + using runtime_type = timing::timer; + using executor_type = executor::current_thread; + + InSequence s; + mock_type m; + now_mock_type nm; + runtime_mock_type rm; + runtime_type r(rm); + executor_type e(r); + + EXPECT_CALL(rm, init_thread()); + EXPECT_CALL(nm, now()) // timing::delay::poll + .WillOnce(Return(time_point(std::chrono::seconds(0)))); + EXPECT_CALL(m, call()); + + EXPECT_CALL(nm, now()) // timing::delay::poll + .WillOnce(Return(time_point(std::chrono::seconds(0)))); + + EXPECT_CALL(nm, now()) // timer::idle + .WillOnce(Return(time_point(std::chrono::seconds(0)))); + EXPECT_CALL(rm, idle(Pointee(time_point(std::chrono::seconds(10))))); + + EXPECT_CALL(nm, now()) // timer::idle + .WillOnce(Return(time_point(std::chrono::seconds(10)))); + EXPECT_CALL(rm, idle(Pointee(time_point(std::chrono::seconds(10))))); + + EXPECT_CALL(nm, now()) // timing::delay::poll + .WillOnce(Return(time_point(std::chrono::seconds(11)))); + EXPECT_CALL(m, call()); + + EXPECT_CALL(nm, now()) // timing::delay::poll + .WillOnce(Return(time_point(std::chrono::seconds(11)))); + + EXPECT_CALL(nm, now()) // timer::idle + .WillOnce(Return(time_point(std::chrono::seconds(15)))); + EXPECT_CALL(rm, idle(Pointee(time_point(std::chrono::seconds(20))))); + + EXPECT_CALL(nm, now()) // timer::idle + .WillOnce(Return(time_point(std::chrono::seconds(49)))); + EXPECT_CALL(rm, idle(Pointee(time_point(std::chrono::seconds(20))))); + + EXPECT_CALL(nm, now()) // timing::delay::poll + .WillOnce(Return(time_point(std::chrono::seconds(49)))); + EXPECT_CALL(m, call()); + + EXPECT_CALL(nm, now()) // timing::delay::poll + .WillOnce(Return(time_point(std::chrono::seconds(49)))); + EXPECT_CALL(m, call()); + + EXPECT_CALL(nm, now()) // timing::delay::poll + .WillOnce(Return(time_point(std::chrono::seconds(49)))); + EXPECT_CALL(m, call()); + EXPECT_CALL(nm, now()) // timing::interval::poll + .WillOnce(Return(time_point(std::chrono::seconds(49)))); + + EXPECT_CALL(nm, now()) // timing::delay::poll + .WillOnce(Return(time_point(std::chrono::seconds(49)))); + + EXPECT_CALL(nm, now()) // timer::idle + .WillOnce(Return(time_point(std::chrono::seconds(49)))); + EXPECT_CALL(rm, idle(Pointee(time_point(std::chrono::seconds(50))))); + + EXPECT_CALL(nm, now()) // timer::idle + .WillOnce(Return(time_point(std::chrono::seconds(50)))); + EXPECT_CALL(rm, idle(Pointee(time_point(std::chrono::seconds(50))))); + + EXPECT_CALL(nm, now()) // timing::interval::poll + .WillOnce(Return(time_point(std::chrono::seconds(50)))); + + e.run( + as_stream( + timing::interval( + time_point(), + std::chrono::seconds(10), + time_point() + std::chrono::seconds(50))) + .for_each([&m]{ + m.call(); + })); } diff --git a/test/asyncpp/timing/interval_tests.cpp b/test/asyncpp/timing/interval_tests.cpp index ba57aaa..87c9d3f 100644 --- a/test/asyncpp/timing/interval_tests.cpp +++ b/test/asyncpp/timing/interval_tests.cpp @@ -21,8 +21,7 @@ TEST(interval_tests, poll) // 30 - 10 = 20 // 20 / 5 = 4 - // 4 - 1 = 3 - // 3 x Ready + // 4 x Ready t.make_current(); @@ -43,7 +42,7 @@ TEST(interval_tests, poll) EXPECT_CALL(m, now) .WillOnce(Return(time_point(std::chrono::seconds(10)))); - auto r2 = s.poll(); + auto r2 = s.poll(); // ready @ 10sec ASSERT_EQ(result_status::ready, r2.status()); EXPECT_CALL(m, now) @@ -55,18 +54,24 @@ TEST(interval_tests, poll) EXPECT_CALL(m, now) .WillOnce(Return(time_point(std::chrono::seconds(30)))); - auto r4 = s.poll(); + auto r4 = s.poll(); // ready @ 15sec ASSERT_EQ(result_status::ready, r4.status()); EXPECT_CALL(m, now) .WillOnce(Return(time_point(std::chrono::seconds(30)))); - auto r5 = s.poll(); + auto r5 = s.poll(); // ready @ 20sec ASSERT_EQ(result_status::ready, r5.status()); EXPECT_CALL(m, now) .WillOnce(Return(time_point(std::chrono::seconds(30)))); - auto r6 = s.poll(); - ASSERT_EQ(result_status::done, r6.status()); + auto r6 = s.poll(); // ready @ 25sec + ASSERT_EQ(result_status::ready, r6.status()); + + EXPECT_CALL(m, now) + .WillOnce(Return(time_point(std::chrono::seconds(30)))); + + auto r7 = s.poll(); + ASSERT_EQ(result_status::done, r7.status()); } diff --git a/test/helper/runtime_mock.h b/test/helper/runtime_mock.h index 935731f..19fa28c 100644 --- a/test/helper/runtime_mock.h +++ b/test/helper/runtime_mock.h @@ -5,8 +5,12 @@ #include +struct runtime_thread_lock + { }; + struct runtime_mock { public: - MOCK_METHOD1(idle, void (const asyncpp::time_point * p_deadline)); + MOCK_METHOD1(idle, void (const asyncpp::time_point * p_deadline)); + MOCK_METHOD0(init_thread, runtime_thread_lock (void)); };