123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686 |
- #ifndef __ASIO2_USER_TIMER_COMPONENT_HPP__
- #define __ASIO2_USER_TIMER_COMPONENT_HPP__
- #if defined(_MSC_VER) && (_MSC_VER >= 1200)
- #pragma once
- #endif
- #include <memory>
- #include <chrono>
- #include <functional>
- #include <unordered_map>
- #include <string>
- #include <string_view>
- #include <future>
- #include <asio2/base/iopool.hpp>
- #include <asio2/base/log.hpp>
- #include <asio2/base/detail/util.hpp>
- #include <asio2/base/detail/allocator.hpp>
- #include <asio2/base/detail/function_traits.hpp>
- namespace asio2::detail
- {
- struct user_timer_handle
- {
- template<typename T, typename = std::void_t<typename std::enable_if_t<!std::is_same_v<
- std::remove_cv_t<std::remove_reference_t<T>>, user_timer_handle>, void>>>
- user_timer_handle(T&& id)
- {
- bind(std::forward<T>(id));
- }
- user_timer_handle(user_timer_handle&& other) = default;
- user_timer_handle(user_timer_handle const& other) = default;
- user_timer_handle& operator=(user_timer_handle&& other) = default;
- user_timer_handle& operator=(user_timer_handle const& other) = default;
- template<typename T>
- inline typename std::enable_if_t<!std::is_same_v<std::remove_cv_t<
- std::remove_reference_t<T>>, user_timer_handle>, void>
- operator=(T&& id)
- {
- bind(std::forward<T>(id));
- }
- inline bool operator==(const user_timer_handle& r) const noexcept
- {
- return (r.handle == this->handle);
- }
- template<typename T>
- inline void bind(T&& id)
- {
- using type = std::remove_cv_t<std::remove_reference_t<T>>;
- using rtype = std::remove_pointer_t<std::remove_all_extents_t<type>>;
- if constexpr (std::is_same_v<std::string, type>)
- {
- handle = std::forward<T>(id);
- }
- else if constexpr (detail::is_string_view_v<type>)
- {
- handle.resize(sizeof(typename type::value_type) * id.size());
- std::memcpy((void*)handle.data(), (const void*)id.data(),
- sizeof(typename type::value_type) * id.size());
- }
- else if constexpr (detail::is_string_v<type>)
- {
- handle.resize(sizeof(typename type::value_type) * id.size());
- std::memcpy((void*)handle.data(), (const void*)id.data(),
- sizeof(typename type::value_type) * id.size());
- }
- else if constexpr (std::is_integral_v<type>)
- {
- handle = std::to_string(id);
- }
- else if constexpr (std::is_floating_point_v<type>)
- {
- handle.resize(sizeof(type));
- std::memcpy((void*)handle.data(), (const void*)&id, sizeof(type));
- }
- else if constexpr (detail::is_char_pointer_v<type>)
- {
- ASIO2_ASSERT(id && (*id));
- if (id)
- {
- std::basic_string_view<rtype> sv{ id };
- handle.resize(sizeof(rtype) * sv.size());
- std::memcpy((void*)handle.data(), (const void*)sv.data(), sizeof(rtype) * sv.size());
- }
- }
- else if constexpr (detail::is_char_array_v<type>)
- {
- std::basic_string_view<rtype> sv{ id };
- handle.resize(sizeof(rtype) * sv.size());
- std::memcpy((void*)handle.data(), (const void*)sv.data(), sizeof(rtype) * sv.size());
- }
- else if constexpr (std::is_pointer_v<type>)
- {
- handle = std::to_string(std::size_t(id));
- }
- else if constexpr (std::is_array_v<type>)
- {
- static_assert(detail::always_false_v<T>);
- }
- else if constexpr (std::is_same_v<user_timer_handle, type>)
- {
- static_assert(detail::always_false_v<T>);
- }
- else
- {
- static_assert(detail::always_false_v<T>);
- }
- }
- std::string handle;
- };
- struct user_timer_handle_hash
- {
- inline std::size_t operator()(const user_timer_handle& k) const noexcept
- {
- return std::hash<std::string>{}(k.handle);
- }
- };
- struct user_timer_handle_equal
- {
- inline bool operator()(const user_timer_handle& lhs, const user_timer_handle& rhs) const noexcept
- {
- return lhs.handle == rhs.handle;
- }
- };
- struct user_timer_obj
- {
- user_timer_handle id;
- asio::steady_timer timer;
- std::function<void()> callback{};
- typename asio::steady_timer::duration interval{};
- mutable std::size_t repeat = static_cast<std::size_t>(-1);
- mutable bool exited = false;
- user_timer_obj(user_timer_handle Id, asio::io_context& context)
- : id(std::move(Id)), timer(context)
- {
- }
- };
- template<class derived_t, class args_t = void>
- class user_timer_cp
- {
- public:
- using user_timer_map = std::unordered_map<user_timer_handle, std::shared_ptr<user_timer_obj>,
- user_timer_handle_hash, user_timer_handle_equal>;
-
- user_timer_cp() {}
-
- ~user_timer_cp() noexcept {}
- public:
-
- template<class TimerId, class IntegerMilliseconds, class Fun, class... Args>
- inline typename std::enable_if_t<is_callable_v<Fun>
- && std::is_integral_v<detail::remove_cvref_t<IntegerMilliseconds>>, void>
- start_timer(TimerId&& timer_id, IntegerMilliseconds interval, Fun&& fun, Args&&... args)
- {
- this->start_timer(std::forward<TimerId>(timer_id), std::chrono::milliseconds(interval), std::size_t(-1),
- std::chrono::milliseconds(interval), std::forward<Fun>(fun), std::forward<Args>(args)...);
- }
-
- template<class TimerId, class IntegerMilliseconds, class Integer, class Fun, class... Args>
- inline typename std::enable_if_t<is_callable_v<Fun>
- && std::is_integral_v<detail::remove_cvref_t<IntegerMilliseconds>>
- && std::is_integral_v<detail::remove_cvref_t<Integer>>, void>
- start_timer(TimerId&& timer_id, IntegerMilliseconds interval, Integer repeat, Fun&& fun, Args&&... args)
- {
- this->start_timer(std::forward<TimerId>(timer_id), std::chrono::milliseconds(interval), repeat,
- std::chrono::milliseconds(interval), std::forward<Fun>(fun), std::forward<Args>(args)...);
- }
-
-
- template<class TimerId, class Rep, class Period, class Fun, class... Args>
- inline typename std::enable_if_t<is_callable_v<Fun>, void>
- start_timer(TimerId&& timer_id, std::chrono::duration<Rep, Period> interval, Fun&& fun, Args&&... args)
- {
- this->start_timer(std::forward<TimerId>(timer_id), interval, std::size_t(-1), interval,
- std::forward<Fun>(fun), std::forward<Args>(args)...);
- }
-
- template<class TimerId, class Rep, class Period, class Integer, class Fun, class... Args>
- inline typename std::enable_if_t<
- is_callable_v<Fun> && std::is_integral_v<detail::remove_cvref_t<Integer>>, void>
- start_timer(TimerId&& timer_id, std::chrono::duration<Rep, Period> interval, Integer repeat,
- Fun&& fun, Args&&... args)
- {
- this->start_timer(std::forward<TimerId>(timer_id), interval, repeat, interval,
- std::forward<Fun>(fun), std::forward<Args>(args)...);
- }
-
- template<class TimerId, class Rep1, class Period1, class Rep2, class Period2, class Fun, class... Args>
- inline typename std::enable_if_t<is_callable_v<Fun>, void>
- start_timer(TimerId&& timer_id, std::chrono::duration<Rep1, Period1> interval,
- std::chrono::duration<Rep2, Period2> first_delay, Fun&& fun, Args&&... args)
- {
- this->start_timer(std::forward<TimerId>(timer_id), interval, std::size_t(-1), first_delay,
- std::forward<Fun>(fun), std::forward<Args>(args)...);
- }
-
- template<class TimerId, class Rep1, class Period1, class Rep2, class Period2, class Integer, class Fun, class... Args>
- inline typename std::enable_if_t<
- is_callable_v<Fun> && std::is_integral_v<detail::remove_cvref_t<Integer>>, void>
- start_timer(TimerId&& timer_id, std::chrono::duration<Rep1, Period1> interval, Integer repeat,
- std::chrono::duration<Rep2, Period2> first_delay, Fun&& fun, Args&&... args)
- {
- if (repeat == static_cast<std::size_t>(0))
- {
- ASIO2_ASSERT(false);
- return;
- }
- if (interval > std::chrono::duration_cast<
- std::chrono::duration<Rep1, Period1>>((asio::steady_timer::duration::max)()))
- interval = std::chrono::duration_cast<std::chrono::duration<Rep1, Period1>>(
- (asio::steady_timer::duration::max)());
- if (first_delay > std::chrono::duration_cast<
- std::chrono::duration<Rep2, Period2>>((asio::steady_timer::duration::max)()))
- first_delay = std::chrono::duration_cast<std::chrono::duration<Rep2, Period2>>(
- (asio::steady_timer::duration::max)());
- derived_t& derive = static_cast<derived_t&>(*this);
- std::function<void()> t = std::bind(std::forward<Fun>(fun), std::forward<Args>(args)...);
-
-
-
- asio::post(derive.io_->context(), make_allocator(derive.wallocator(),
- [this, &derive, this_ptr = derive.selfptr(),
- timer_handle = user_timer_handle(std::forward<TimerId>(timer_id)),
- interval, repeat, first_delay, callback = std::move(t)]() mutable
- {
-
- auto iter = this->user_timers_.find(timer_handle);
- if (iter != this->user_timers_.end())
- {
- iter->second->exited = true;
- detail::cancel_timer(iter->second->timer);
- }
-
- std::shared_ptr<user_timer_obj> timer_obj_ptr = std::make_shared<user_timer_obj>(
- timer_handle, derive.io_->context());
-
-
-
- timer_obj_ptr->callback = std::move(callback);
- timer_obj_ptr->interval = interval;
- timer_obj_ptr->repeat = static_cast<std::size_t>(repeat);
- this->user_timers_[std::move(timer_handle)] = timer_obj_ptr;
- derive.io_->timers().emplace(std::addressof(timer_obj_ptr->timer));
- derive._post_user_timers(std::move(this_ptr), std::move(timer_obj_ptr), first_delay);
- }));
- }
-
- template<class TimerId>
- inline void stop_timer(TimerId&& timer_id)
- {
- derived_t& derive = static_cast<derived_t&>(*this);
-
-
-
-
-
-
-
-
- asio::post(derive.io_->context(), make_allocator(derive.wallocator(),
- [this, this_ptr = derive.selfptr(), timer_id = std::forward<TimerId>(timer_id)]
- () mutable
- {
- detail::ignore_unused(this_ptr);
- auto iter = this->user_timers_.find(timer_id);
- if (iter != this->user_timers_.end())
- {
- iter->second->exited = true;
- detail::cancel_timer(iter->second->timer);
- this->user_timers_.erase(iter);
- }
- }));
- }
-
- inline void stop_all_timers()
- {
- derived_t& derive = static_cast<derived_t&>(*this);
-
-
- asio::post(derive.io_->context(), make_allocator(derive.wallocator(),
- [this, this_ptr = derive.selfptr()]() mutable
- {
-
- for (auto &[id, timer_obj_ptr] : this->user_timers_)
- {
- detail::ignore_unused(this_ptr, id);
- timer_obj_ptr->exited = true;
- detail::cancel_timer(timer_obj_ptr->timer);
- }
- this->user_timers_.clear();
- }));
- }
-
- template<class TimerId>
- inline bool is_timer_exists(TimerId&& timer_id)
- {
- derived_t& derive = static_cast<derived_t&>(*this);
- if (derive.io_->running_in_this_thread())
- {
- return (this->user_timers_.find(timer_id) != this->user_timers_.end());
- }
- std::promise<bool> prm;
- std::future<bool> fut = prm.get_future();
- asio::post(derive.io_->context(), make_allocator(derive.wallocator(),
- [this, this_ptr = derive.selfptr(), timer_id = std::forward<TimerId>(timer_id), &prm]
- () mutable
- {
- detail::ignore_unused(this_ptr);
- prm.set_value(this->user_timers_.find(timer_id) != this->user_timers_.end());
- }));
- return fut.get();
- }
-
- template<class TimerId>
- inline typename asio::steady_timer::duration get_timer_interval(TimerId&& timer_id)
- {
- derived_t& derive = static_cast<derived_t&>(*this);
- if (derive.io_->running_in_this_thread())
- {
- auto iter = this->user_timers_.find(timer_id);
- if (iter != this->user_timers_.end())
- {
- return iter->second->interval;
- }
- else
- {
- return typename asio::steady_timer::duration{};
- }
- }
- std::promise<typename asio::steady_timer::duration> prm;
- std::future<typename asio::steady_timer::duration> fut = prm.get_future();
- asio::post(derive.io_->context(), make_allocator(derive.wallocator(),
- [this, this_ptr = derive.selfptr(), timer_id = std::forward<TimerId>(timer_id), &prm]
- () mutable
- {
- detail::ignore_unused(this_ptr);
- auto iter = this->user_timers_.find(timer_id);
- if (iter != this->user_timers_.end())
- {
- prm.set_value(iter->second->interval);
- }
- else
- {
- prm.set_value(typename asio::steady_timer::duration{});
- }
- }));
- return fut.get();
- }
-
- template<class TimerId, class Rep, class Period>
- inline void set_timer_interval(TimerId&& timer_id, std::chrono::duration<Rep, Period> interval)
- {
- derived_t& derive = static_cast<derived_t&>(*this);
- if (interval > std::chrono::duration_cast<
- std::chrono::duration<Rep, Period>>((asio::steady_timer::duration::max)()))
- interval = std::chrono::duration_cast<std::chrono::duration<Rep, Period>>(
- (asio::steady_timer::duration::max)());
- if (derive.io_->running_in_this_thread())
- {
- auto iter = this->user_timers_.find(timer_id);
- if (iter != this->user_timers_.end())
- {
- iter->second->interval = interval;
- }
- return;
- }
- asio::dispatch(derive.io_->context(), make_allocator(derive.wallocator(),
- [this, this_ptr = derive.selfptr(), timer_id = std::forward<TimerId>(timer_id), interval]
- () mutable
- {
- detail::ignore_unused(this_ptr);
- auto iter = this->user_timers_.find(timer_id);
- if (iter != this->user_timers_.end())
- {
- iter->second->interval = interval;
- }
- }));
- }
-
- template<class TimerId, class IntegerMilliseconds>
- inline typename std::enable_if_t<std::is_integral_v<detail::remove_cvref_t<IntegerMilliseconds>>, void>
- set_timer_interval(TimerId&& timer_id, IntegerMilliseconds interval)
- {
- derived_t& derive = static_cast<derived_t&>(*this);
- return derive.set_timer_interval(std::forward<TimerId>(timer_id), std::chrono::milliseconds(interval));
- }
-
- template<class TimerId, class Rep, class Period>
- inline void reset_timer_interval(TimerId&& timer_id, std::chrono::duration<Rep, Period> interval)
- {
- derived_t& derive = static_cast<derived_t&>(*this);
- return derive.set_timer_interval(std::forward<TimerId>(timer_id), interval);
- }
-
- template<class TimerId, class IntegerMilliseconds>
- inline typename std::enable_if_t<std::is_integral_v<detail::remove_cvref_t<IntegerMilliseconds>>, void>
- reset_timer_interval(TimerId&& timer_id, IntegerMilliseconds interval)
- {
- derived_t& derive = static_cast<derived_t&>(*this);
- return derive.set_timer_interval(std::forward<TimerId>(timer_id), interval);
- }
- protected:
-
- inline void _dispatch_stop_all_timers()
- {
- derived_t& derive = static_cast<derived_t&>(*this);
-
-
- asio::dispatch(derive.io_->context(), make_allocator(derive.wallocator(),
- [this, this_ptr = derive.selfptr()]() mutable
- {
-
- for (auto &[id, timer_obj_ptr] : this->user_timers_)
- {
- detail::ignore_unused(this_ptr, id);
- timer_obj_ptr->exited = true;
- detail::cancel_timer(timer_obj_ptr->timer);
- }
- this->user_timers_.clear();
- }));
- }
- protected:
- template<class Rep, class Period>
- inline void _post_user_timers(std::shared_ptr<derived_t> this_ptr,
- std::shared_ptr<user_timer_obj> timer_obj_ptr, std::chrono::duration<Rep, Period> expiry)
- {
- derived_t& derive = static_cast<derived_t&>(*this);
- ASIO2_ASSERT(this->user_timers_.find(timer_obj_ptr->id) != this->user_timers_.end());
- asio::steady_timer& timer = timer_obj_ptr->timer;
- timer.expires_after(expiry);
- timer.async_wait(
- [&derive, this_ptr = std::move(this_ptr), timer_ptr = std::move(timer_obj_ptr)]
- (const error_code& ec) mutable
- {
- derive._handle_user_timers(ec, std::move(this_ptr), std::move(timer_ptr));
- });
- }
- inline void _handle_user_timers(error_code ec, std::shared_ptr<derived_t> this_ptr,
- std::shared_ptr<user_timer_obj> timer_obj_ptr)
- {
- derived_t& derive = static_cast<derived_t&>(*this);
- ASIO2_ASSERT((!ec) || ec == asio::error::operation_aborted);
- if (timer_obj_ptr->exited && (!ec))
- {
- ec = asio::error::operation_aborted;
- }
- set_last_error(ec);
-
-
-
-
-
-
-
- if (timer_obj_ptr->repeat == static_cast<std::size_t>(-1))
- {
- derive._invoke_user_timer_callback(ec, timer_obj_ptr);
- }
- else
- {
- ASIO2_ASSERT(timer_obj_ptr->repeat > static_cast<std::size_t>(0));
- derive._invoke_user_timer_callback(ec, timer_obj_ptr);
- timer_obj_ptr->repeat--;
- }
-
-
-
-
- if (timer_obj_ptr->exited)
- {
-
-
- derive.io_->timers().erase(std::addressof(timer_obj_ptr->timer));
- return;
- }
- if (ec == asio::error::operation_aborted || timer_obj_ptr->repeat == static_cast<std::size_t>(0))
- {
- derive.io_->timers().erase(std::addressof(timer_obj_ptr->timer));
- auto iter = this->user_timers_.find(timer_obj_ptr->id);
- if (iter != this->user_timers_.end())
- {
- iter->second->exited = true;
- this->user_timers_.erase(iter);
- }
- return;
- }
- typename asio::steady_timer::duration expiry = timer_obj_ptr->interval;
- derive._post_user_timers(std::move(this_ptr), std::move(timer_obj_ptr), expiry);
- }
- inline void _invoke_user_timer_callback(const error_code& ec, std::shared_ptr<user_timer_obj>& timer_obj_ptr)
- {
- detail::ignore_unused(ec);
- #if defined(ASIO2_ENABLE_TIMER_CALLBACK_WHEN_ERROR)
- (timer_obj_ptr->callback)();
- #else
- if (!ec)
- {
- (timer_obj_ptr->callback)();
- }
- #endif
- }
- protected:
-
- user_timer_map user_timers_;
- };
- }
- #endif
|