user_timer_cp.hpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. /*
  2. * Copyright (c) 2017-2023 zhllxt
  3. *
  4. * author : zhllxt
  5. * email : 37792738@qq.com
  6. *
  7. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. */
  10. #ifndef __ASIO2_USER_TIMER_COMPONENT_HPP__
  11. #define __ASIO2_USER_TIMER_COMPONENT_HPP__
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. #pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <memory>
  16. #include <chrono>
  17. #include <functional>
  18. #include <unordered_map>
  19. #include <string>
  20. #include <string_view>
  21. #include <future>
  22. #include <asio2/base/iopool.hpp>
  23. #include <asio2/base/log.hpp>
  24. #include <asio2/base/detail/util.hpp>
  25. #include <asio2/base/detail/allocator.hpp>
  26. #include <asio2/base/detail/function_traits.hpp>
  27. namespace asio2::detail
  28. {
  29. struct user_timer_handle
  30. {
  31. template<typename T, typename = std::void_t<typename std::enable_if_t<!std::is_same_v<
  32. std::remove_cv_t<std::remove_reference_t<T>>, user_timer_handle>, void>>>
  33. user_timer_handle(T&& id)
  34. {
  35. bind(std::forward<T>(id));
  36. }
  37. user_timer_handle(user_timer_handle&& other) = default;
  38. user_timer_handle(user_timer_handle const& other) = default;
  39. user_timer_handle& operator=(user_timer_handle&& other) = default;
  40. user_timer_handle& operator=(user_timer_handle const& other) = default;
  41. template<typename T>
  42. inline typename std::enable_if_t<!std::is_same_v<std::remove_cv_t<
  43. std::remove_reference_t<T>>, user_timer_handle>, void>
  44. operator=(T&& id)
  45. {
  46. bind(std::forward<T>(id));
  47. }
  48. inline bool operator==(const user_timer_handle& r) const noexcept
  49. {
  50. return (r.handle == this->handle);
  51. }
  52. template<typename T>
  53. inline void bind(T&& id)
  54. {
  55. using type = std::remove_cv_t<std::remove_reference_t<T>>;
  56. using rtype = std::remove_pointer_t<std::remove_all_extents_t<type>>;
  57. if /**/ constexpr (std::is_same_v<std::string, type>)
  58. {
  59. handle = std::forward<T>(id);
  60. }
  61. else if constexpr (detail::is_string_view_v<type>)
  62. {
  63. handle.resize(sizeof(typename type::value_type) * id.size());
  64. std::memcpy((void*)handle.data(), (const void*)id.data(),
  65. sizeof(typename type::value_type) * id.size());
  66. }
  67. else if constexpr (detail::is_string_v<type>)
  68. {
  69. handle.resize(sizeof(typename type::value_type) * id.size());
  70. std::memcpy((void*)handle.data(), (const void*)id.data(),
  71. sizeof(typename type::value_type) * id.size());
  72. }
  73. else if constexpr (std::is_integral_v<type>)
  74. {
  75. handle = std::to_string(id);
  76. }
  77. else if constexpr (std::is_floating_point_v<type>)
  78. {
  79. handle.resize(sizeof(type));
  80. std::memcpy((void*)handle.data(), (const void*)&id, sizeof(type));
  81. }
  82. else if constexpr (detail::is_char_pointer_v<type>)
  83. {
  84. ASIO2_ASSERT(id && (*id));
  85. if (id)
  86. {
  87. std::basic_string_view<rtype> sv{ id };
  88. handle.resize(sizeof(rtype) * sv.size());
  89. std::memcpy((void*)handle.data(), (const void*)sv.data(), sizeof(rtype) * sv.size());
  90. }
  91. }
  92. else if constexpr (detail::is_char_array_v<type>)
  93. {
  94. std::basic_string_view<rtype> sv{ id };
  95. handle.resize(sizeof(rtype) * sv.size());
  96. std::memcpy((void*)handle.data(), (const void*)sv.data(), sizeof(rtype) * sv.size());
  97. }
  98. else if constexpr (std::is_pointer_v<type>)
  99. {
  100. handle = std::to_string(std::size_t(id));
  101. }
  102. else if constexpr (std::is_array_v<type>)
  103. {
  104. static_assert(detail::always_false_v<T>);
  105. }
  106. else if constexpr (std::is_same_v<user_timer_handle, type>)
  107. {
  108. static_assert(detail::always_false_v<T>);
  109. }
  110. else
  111. {
  112. static_assert(detail::always_false_v<T>);
  113. }
  114. }
  115. std::string handle;
  116. };
  117. struct user_timer_handle_hash
  118. {
  119. inline std::size_t operator()(const user_timer_handle& k) const noexcept
  120. {
  121. return std::hash<std::string>{}(k.handle);
  122. }
  123. };
  124. struct user_timer_handle_equal
  125. {
  126. inline bool operator()(const user_timer_handle& lhs, const user_timer_handle& rhs) const noexcept
  127. {
  128. return lhs.handle == rhs.handle;
  129. }
  130. };
  131. struct user_timer_obj
  132. {
  133. user_timer_handle id;
  134. asio::steady_timer timer;
  135. std::function<void()> callback{};
  136. typename asio::steady_timer::duration interval{};
  137. mutable std::size_t repeat = static_cast<std::size_t>(-1);
  138. mutable bool exited = false;
  139. user_timer_obj(user_timer_handle Id, asio::io_context& context)
  140. : id(std::move(Id)), timer(context)
  141. {
  142. }
  143. };
  144. template<class derived_t, class args_t = void>
  145. class user_timer_cp
  146. {
  147. public:
  148. using user_timer_map = std::unordered_map<user_timer_handle, std::shared_ptr<user_timer_obj>,
  149. user_timer_handle_hash, user_timer_handle_equal>;
  150. /**
  151. * @brief constructor
  152. */
  153. user_timer_cp() {}
  154. /**
  155. * @brief destructor
  156. */
  157. ~user_timer_cp() noexcept {}
  158. public:
  159. /**
  160. * @brief start a timer
  161. * @param timer_id - The timer id can be integer or string, example : 1,2,3 or "id1" "id2",
  162. * If a timer with this id already exists, that timer will be forced to canceled.
  163. * @param interval - An integer millisecond value for the amount of time between set and firing.
  164. * @param fun - The callback function signature : [](){}
  165. */
  166. template<class TimerId, class IntegerMilliseconds, class Fun, class... Args>
  167. inline typename std::enable_if_t<is_callable_v<Fun>
  168. && std::is_integral_v<detail::remove_cvref_t<IntegerMilliseconds>>, void>
  169. start_timer(TimerId&& timer_id, IntegerMilliseconds interval, Fun&& fun, Args&&... args)
  170. {
  171. this->start_timer(std::forward<TimerId>(timer_id), std::chrono::milliseconds(interval), std::size_t(-1),
  172. std::chrono::milliseconds(interval), std::forward<Fun>(fun), std::forward<Args>(args)...);
  173. }
  174. /**
  175. * @brief start a timer
  176. * @param timer_id - The timer id can be integer or string, example : 1,2,3 or "id1" "id2",
  177. * If a timer with this id already exists, that timer will be forced to canceled.
  178. * @param interval - An integer millisecond value for the amount of time between set and firing.
  179. * @param repeat - An integer value to indicate the number of times the timer is repeated.
  180. * @param fun - The callback function signature : [](){}
  181. */
  182. template<class TimerId, class IntegerMilliseconds, class Integer, class Fun, class... Args>
  183. inline typename std::enable_if_t<is_callable_v<Fun>
  184. && std::is_integral_v<detail::remove_cvref_t<IntegerMilliseconds>>
  185. && std::is_integral_v<detail::remove_cvref_t<Integer>>, void>
  186. start_timer(TimerId&& timer_id, IntegerMilliseconds interval, Integer repeat, Fun&& fun, Args&&... args)
  187. {
  188. this->start_timer(std::forward<TimerId>(timer_id), std::chrono::milliseconds(interval), repeat,
  189. std::chrono::milliseconds(interval), std::forward<Fun>(fun), std::forward<Args>(args)...);
  190. }
  191. // ----------------------------------------------------------------------------------------
  192. /**
  193. * @brief start a timer
  194. * @param timer_id - The timer id can be integer or string, example : 1,2,3 or "id1" "id2",
  195. * If a timer with this id already exists, that timer will be forced to canceled.
  196. * @param interval - The amount of time between set and firing.
  197. * @param fun - The callback function signature : [](){}
  198. */
  199. template<class TimerId, class Rep, class Period, class Fun, class... Args>
  200. inline typename std::enable_if_t<is_callable_v<Fun>, void>
  201. start_timer(TimerId&& timer_id, std::chrono::duration<Rep, Period> interval, Fun&& fun, Args&&... args)
  202. {
  203. this->start_timer(std::forward<TimerId>(timer_id), interval, std::size_t(-1), interval,
  204. std::forward<Fun>(fun), std::forward<Args>(args)...);
  205. }
  206. /**
  207. * @brief start a timer
  208. * @param timer_id - The timer id can be integer or string, example : 1,2,3 or "id1" "id2",
  209. * If a timer with this id already exists, that timer will be forced to canceled.
  210. * @param interval - The amount of time between set and firing.
  211. * @param repeat - An integer value to indicate the number of times the timer is repeated.
  212. * @param fun - The callback function signature : [](){}
  213. */
  214. template<class TimerId, class Rep, class Period, class Integer, class Fun, class... Args>
  215. inline typename std::enable_if_t<
  216. is_callable_v<Fun> && std::is_integral_v<detail::remove_cvref_t<Integer>>, void>
  217. start_timer(TimerId&& timer_id, std::chrono::duration<Rep, Period> interval, Integer repeat,
  218. Fun&& fun, Args&&... args)
  219. {
  220. this->start_timer(std::forward<TimerId>(timer_id), interval, repeat, interval,
  221. std::forward<Fun>(fun), std::forward<Args>(args)...);
  222. }
  223. /**
  224. * @brief start a timer
  225. * @param timer_id - The timer id can be integer or string, example : 1,2,3 or "id1" "id2",
  226. * If a timer with this id already exists, that timer will be forced to canceled.
  227. * @param interval - The amount of time between set and firing.
  228. * @param first_delay - The timeout for the first execute of timer.
  229. * @param fun - The callback function signature : [](){}
  230. */
  231. template<class TimerId, class Rep1, class Period1, class Rep2, class Period2, class Fun, class... Args>
  232. inline typename std::enable_if_t<is_callable_v<Fun>, void>
  233. start_timer(TimerId&& timer_id, std::chrono::duration<Rep1, Period1> interval,
  234. std::chrono::duration<Rep2, Period2> first_delay, Fun&& fun, Args&&... args)
  235. {
  236. this->start_timer(std::forward<TimerId>(timer_id), interval, std::size_t(-1), first_delay,
  237. std::forward<Fun>(fun), std::forward<Args>(args)...);
  238. }
  239. /**
  240. * @brief start a timer
  241. * @param timer_id - The timer id can be integer or string, example : 1,2,3 or "id1" "id2",
  242. * If a timer with this id already exists, that timer will be forced to canceled.
  243. * @param interval - The amount of time between set and firing.
  244. * @param repeat - Total number of times the timer is executed.
  245. * @param first_delay - The timeout for the first execute of timer.
  246. * @param fun - The callback function signature : [](){}
  247. */
  248. template<class TimerId, class Rep1, class Period1, class Rep2, class Period2, class Integer, class Fun, class... Args>
  249. inline typename std::enable_if_t<
  250. is_callable_v<Fun> && std::is_integral_v<detail::remove_cvref_t<Integer>>, void>
  251. start_timer(TimerId&& timer_id, std::chrono::duration<Rep1, Period1> interval, Integer repeat,
  252. std::chrono::duration<Rep2, Period2> first_delay, Fun&& fun, Args&&... args)
  253. {
  254. if (repeat == static_cast<std::size_t>(0))
  255. {
  256. ASIO2_ASSERT(false);
  257. return;
  258. }
  259. if (interval > std::chrono::duration_cast<
  260. std::chrono::duration<Rep1, Period1>>((asio::steady_timer::duration::max)()))
  261. interval = std::chrono::duration_cast<std::chrono::duration<Rep1, Period1>>(
  262. (asio::steady_timer::duration::max)());
  263. if (first_delay > std::chrono::duration_cast<
  264. std::chrono::duration<Rep2, Period2>>((asio::steady_timer::duration::max)()))
  265. first_delay = std::chrono::duration_cast<std::chrono::duration<Rep2, Period2>>(
  266. (asio::steady_timer::duration::max)());
  267. derived_t& derive = static_cast<derived_t&>(*this);
  268. std::function<void()> t = std::bind(std::forward<Fun>(fun), std::forward<Args>(args)...);
  269. // Whether or not we run on the io_context thread, We all start the timer by post an asynchronous
  270. // event, in order to avoid unexpected problems caused by the user start or stop the
  271. // timer again in the timer callback function.
  272. asio::post(derive.io_->context(), make_allocator(derive.wallocator(),
  273. [this, &derive, this_ptr = derive.selfptr(),
  274. timer_handle = user_timer_handle(std::forward<TimerId>(timer_id)),
  275. interval, repeat, first_delay, callback = std::move(t)]() mutable
  276. {
  277. // if the timer is already exists, cancel it first.
  278. auto iter = this->user_timers_.find(timer_handle);
  279. if (iter != this->user_timers_.end())
  280. {
  281. iter->second->exited = true;
  282. detail::cancel_timer(iter->second->timer);
  283. }
  284. // the asio::steady_timer's constructor may be throw some exception.
  285. std::shared_ptr<user_timer_obj> timer_obj_ptr = std::make_shared<user_timer_obj>(
  286. timer_handle, derive.io_->context());
  287. // after asio::steady_timer's constructor is successed, then set the callback,
  288. // avoid the callback is empty by std::move(callback) when asio::steady_timer's
  289. // constructor throw some exception.
  290. timer_obj_ptr->callback = std::move(callback);
  291. timer_obj_ptr->interval = interval;
  292. timer_obj_ptr->repeat = static_cast<std::size_t>(repeat);
  293. this->user_timers_[std::move(timer_handle)] = timer_obj_ptr;
  294. derive.io_->timers().emplace(std::addressof(timer_obj_ptr->timer));
  295. derive._post_user_timers(std::move(this_ptr), std::move(timer_obj_ptr), first_delay);
  296. }));
  297. }
  298. /**
  299. * @brief stop the timer by timer id
  300. */
  301. template<class TimerId>
  302. inline void stop_timer(TimerId&& timer_id)
  303. {
  304. derived_t& derive = static_cast<derived_t&>(*this);
  305. // must use post, otherwise when call stop_timer immediately after start_timer
  306. // will cause the stop_timer has no effect.
  307. // can't use dispatch, otherwise if call stop_timer in timer callback, it will
  308. // cause this : the stop_timer will cancel the timer, after stop_timer, the callback
  309. // will executed continue, then it will post next timer, beacuse the cancel is
  310. // before the post next timer, so the cancel will has no effect. (so there was
  311. // a flag "exit" to ensure this : even if use dispatch, the timer also can be
  312. // canceled success)
  313. asio::post(derive.io_->context(), make_allocator(derive.wallocator(),
  314. [this, this_ptr = derive.selfptr(), timer_id = std::forward<TimerId>(timer_id)]
  315. () mutable
  316. {
  317. detail::ignore_unused(this_ptr);
  318. auto iter = this->user_timers_.find(timer_id);
  319. if (iter != this->user_timers_.end())
  320. {
  321. iter->second->exited = true;
  322. detail::cancel_timer(iter->second->timer);
  323. this->user_timers_.erase(iter);
  324. }
  325. }));
  326. }
  327. /**
  328. * @brief stop all timers
  329. */
  330. inline void stop_all_timers()
  331. {
  332. derived_t& derive = static_cast<derived_t&>(*this);
  333. // must use post, otherwise when call stop_all_timers immediately after start_timer
  334. // will cause the stop_all_timers has no effect.
  335. asio::post(derive.io_->context(), make_allocator(derive.wallocator(),
  336. [this, this_ptr = derive.selfptr()]() mutable
  337. {
  338. // close user custom timers
  339. for (auto &[id, timer_obj_ptr] : this->user_timers_)
  340. {
  341. detail::ignore_unused(this_ptr, id);
  342. timer_obj_ptr->exited = true;
  343. detail::cancel_timer(timer_obj_ptr->timer);
  344. }
  345. this->user_timers_.clear();
  346. }));
  347. }
  348. /**
  349. * @brief Returns true if the specified timer exists
  350. */
  351. template<class TimerId>
  352. inline bool is_timer_exists(TimerId&& timer_id)
  353. {
  354. derived_t& derive = static_cast<derived_t&>(*this);
  355. if (derive.io_->running_in_this_thread())
  356. {
  357. return (this->user_timers_.find(timer_id) != this->user_timers_.end());
  358. }
  359. std::promise<bool> prm;
  360. std::future<bool> fut = prm.get_future();
  361. asio::post(derive.io_->context(), make_allocator(derive.wallocator(),
  362. [this, this_ptr = derive.selfptr(), timer_id = std::forward<TimerId>(timer_id), &prm]
  363. () mutable
  364. {
  365. detail::ignore_unused(this_ptr);
  366. prm.set_value(this->user_timers_.find(timer_id) != this->user_timers_.end());
  367. }));
  368. return fut.get();
  369. }
  370. /**
  371. * @brief Get the interval for the specified timer.
  372. */
  373. template<class TimerId>
  374. inline typename asio::steady_timer::duration get_timer_interval(TimerId&& timer_id)
  375. {
  376. derived_t& derive = static_cast<derived_t&>(*this);
  377. if (derive.io_->running_in_this_thread())
  378. {
  379. auto iter = this->user_timers_.find(timer_id);
  380. if (iter != this->user_timers_.end())
  381. {
  382. return iter->second->interval;
  383. }
  384. else
  385. {
  386. return typename asio::steady_timer::duration{};
  387. }
  388. }
  389. std::promise<typename asio::steady_timer::duration> prm;
  390. std::future<typename asio::steady_timer::duration> fut = prm.get_future();
  391. asio::post(derive.io_->context(), make_allocator(derive.wallocator(),
  392. [this, this_ptr = derive.selfptr(), timer_id = std::forward<TimerId>(timer_id), &prm]
  393. () mutable
  394. {
  395. detail::ignore_unused(this_ptr);
  396. auto iter = this->user_timers_.find(timer_id);
  397. if (iter != this->user_timers_.end())
  398. {
  399. prm.set_value(iter->second->interval);
  400. }
  401. else
  402. {
  403. prm.set_value(typename asio::steady_timer::duration{});
  404. }
  405. }));
  406. return fut.get();
  407. }
  408. /**
  409. * @brief Set the interval for the specified timer.
  410. */
  411. template<class TimerId, class Rep, class Period>
  412. inline void set_timer_interval(TimerId&& timer_id, std::chrono::duration<Rep, Period> interval)
  413. {
  414. derived_t& derive = static_cast<derived_t&>(*this);
  415. if (interval > std::chrono::duration_cast<
  416. std::chrono::duration<Rep, Period>>((asio::steady_timer::duration::max)()))
  417. interval = std::chrono::duration_cast<std::chrono::duration<Rep, Period>>(
  418. (asio::steady_timer::duration::max)());
  419. if (derive.io_->running_in_this_thread())
  420. {
  421. auto iter = this->user_timers_.find(timer_id);
  422. if (iter != this->user_timers_.end())
  423. {
  424. iter->second->interval = interval;
  425. }
  426. return;
  427. }
  428. asio::dispatch(derive.io_->context(), make_allocator(derive.wallocator(),
  429. [this, this_ptr = derive.selfptr(), timer_id = std::forward<TimerId>(timer_id), interval]
  430. () mutable
  431. {
  432. detail::ignore_unused(this_ptr);
  433. auto iter = this->user_timers_.find(timer_id);
  434. if (iter != this->user_timers_.end())
  435. {
  436. iter->second->interval = interval;
  437. }
  438. }));
  439. }
  440. /**
  441. * @brief Set the interval for the specified timer.
  442. */
  443. template<class TimerId, class IntegerMilliseconds>
  444. inline typename std::enable_if_t<std::is_integral_v<detail::remove_cvref_t<IntegerMilliseconds>>, void>
  445. set_timer_interval(TimerId&& timer_id, IntegerMilliseconds interval)
  446. {
  447. derived_t& derive = static_cast<derived_t&>(*this);
  448. return derive.set_timer_interval(std::forward<TimerId>(timer_id), std::chrono::milliseconds(interval));
  449. }
  450. /**
  451. * @brief Reset the interval for the specified timer.
  452. */
  453. template<class TimerId, class Rep, class Period>
  454. inline void reset_timer_interval(TimerId&& timer_id, std::chrono::duration<Rep, Period> interval)
  455. {
  456. derived_t& derive = static_cast<derived_t&>(*this);
  457. return derive.set_timer_interval(std::forward<TimerId>(timer_id), interval);
  458. }
  459. /**
  460. * @brief Reset the interval for the specified timer.
  461. */
  462. template<class TimerId, class IntegerMilliseconds>
  463. inline typename std::enable_if_t<std::is_integral_v<detail::remove_cvref_t<IntegerMilliseconds>>, void>
  464. reset_timer_interval(TimerId&& timer_id, IntegerMilliseconds interval)
  465. {
  466. derived_t& derive = static_cast<derived_t&>(*this);
  467. return derive.set_timer_interval(std::forward<TimerId>(timer_id), interval);
  468. }
  469. protected:
  470. /**
  471. * @brief stop all timers
  472. * Use dispatch instead of post, this function is used for inner.
  473. */
  474. inline void _dispatch_stop_all_timers()
  475. {
  476. derived_t& derive = static_cast<derived_t&>(*this);
  477. // must use post, otherwise when call stop_all_timers immediately after start_timer
  478. // will cause the stop_all_timers has no effect.
  479. asio::dispatch(derive.io_->context(), make_allocator(derive.wallocator(),
  480. [this, this_ptr = derive.selfptr()]() mutable
  481. {
  482. // close user custom timers
  483. for (auto &[id, timer_obj_ptr] : this->user_timers_)
  484. {
  485. detail::ignore_unused(this_ptr, id);
  486. timer_obj_ptr->exited = true;
  487. detail::cancel_timer(timer_obj_ptr->timer);
  488. }
  489. this->user_timers_.clear();
  490. }));
  491. }
  492. protected:
  493. template<class Rep, class Period>
  494. inline void _post_user_timers(std::shared_ptr<derived_t> this_ptr,
  495. std::shared_ptr<user_timer_obj> timer_obj_ptr, std::chrono::duration<Rep, Period> expiry)
  496. {
  497. derived_t& derive = static_cast<derived_t&>(*this);
  498. ASIO2_ASSERT(this->user_timers_.find(timer_obj_ptr->id) != this->user_timers_.end());
  499. asio::steady_timer& timer = timer_obj_ptr->timer;
  500. timer.expires_after(expiry);
  501. timer.async_wait(
  502. [&derive, this_ptr = std::move(this_ptr), timer_ptr = std::move(timer_obj_ptr)]
  503. (const error_code& ec) mutable
  504. {
  505. derive._handle_user_timers(ec, std::move(this_ptr), std::move(timer_ptr));
  506. });
  507. }
  508. inline void _handle_user_timers(error_code ec, std::shared_ptr<derived_t> this_ptr,
  509. std::shared_ptr<user_timer_obj> timer_obj_ptr)
  510. {
  511. derived_t& derive = static_cast<derived_t&>(*this);
  512. ASIO2_ASSERT((!ec) || ec == asio::error::operation_aborted);
  513. if (timer_obj_ptr->exited && (!ec))
  514. {
  515. ec = asio::error::operation_aborted;
  516. }
  517. set_last_error(ec);
  518. // Should the callback function also be called if there is an error in the timer ?
  519. // It made me difficult to choose.After many adjustments and the requirements of the
  520. // actual project, it is more reasonable to still call the callback function when
  521. // there are some errors.
  522. // eg. user call start_timer, then call stop_timer after a later, but the timer's
  523. // timeout is not reached, perhaps the user wants the timer callback function also
  524. // can be called even if the timer is aborted by stop_timer.
  525. if (timer_obj_ptr->repeat == static_cast<std::size_t>(-1))
  526. {
  527. derive._invoke_user_timer_callback(ec, timer_obj_ptr);
  528. }
  529. else
  530. {
  531. ASIO2_ASSERT(timer_obj_ptr->repeat > static_cast<std::size_t>(0));
  532. derive._invoke_user_timer_callback(ec, timer_obj_ptr);
  533. timer_obj_ptr->repeat--;
  534. }
  535. /**
  536. * @note If the timer has already expired when cancel() is called, then the
  537. * handlers for asynchronous wait operations will:
  538. *
  539. * @li have already been invoked; or
  540. *
  541. * @li have been queued for invocation in the near future.
  542. *
  543. * These handlers can no longer be cancelled, and therefore are passed an
  544. * error code that indicates the successful completion of the wait operation.
  545. */
  546. // must detect whether the timer is still exists by "exited", in some cases,
  547. // after remove and cancel a timer, the ec is 0 (not operation_aborted) and
  548. // the steady_timer is still exist
  549. if (timer_obj_ptr->exited)
  550. {
  551. // if exited is true, can't erase the timer object from the "user_timers_",
  552. // beacuse maybe user start timer multi times with same id.
  553. derive.io_->timers().erase(std::addressof(timer_obj_ptr->timer));
  554. return;
  555. }
  556. if (ec == asio::error::operation_aborted || timer_obj_ptr->repeat == static_cast<std::size_t>(0))
  557. {
  558. derive.io_->timers().erase(std::addressof(timer_obj_ptr->timer));
  559. auto iter = this->user_timers_.find(timer_obj_ptr->id);
  560. if (iter != this->user_timers_.end())
  561. {
  562. iter->second->exited = true;
  563. this->user_timers_.erase(iter);
  564. }
  565. return;
  566. }
  567. typename asio::steady_timer::duration expiry = timer_obj_ptr->interval;
  568. derive._post_user_timers(std::move(this_ptr), std::move(timer_obj_ptr), expiry);
  569. }
  570. inline void _invoke_user_timer_callback(const error_code& ec, std::shared_ptr<user_timer_obj>& timer_obj_ptr)
  571. {
  572. detail::ignore_unused(ec);
  573. #if defined(ASIO2_ENABLE_TIMER_CALLBACK_WHEN_ERROR)
  574. (timer_obj_ptr->callback)();
  575. #else
  576. if (!ec)
  577. {
  578. (timer_obj_ptr->callback)();
  579. }
  580. #endif
  581. }
  582. protected:
  583. /// user-defined timer
  584. user_timer_map user_timers_;
  585. };
  586. }
  587. #endif // !__ASIO2_USER_TIMER_COMPONENT_HPP__