/src/crow/include/crow/task_timer.h
Line  | Count  | Source  | 
1  |  | #pragma once  | 
2  |  |  | 
3  |  | #ifdef CROW_USE_BOOST  | 
4  |  | #include <boost/asio.hpp>  | 
5  |  | #include <boost/asio/basic_waitable_timer.hpp>  | 
6  |  | #else  | 
7  |  | #ifndef ASIO_STANDALONE  | 
8  |  | #define ASIO_STANDALONE  | 
9  |  | #endif  | 
10  |  | #include <asio.hpp>  | 
11  |  | #include <asio/basic_waitable_timer.hpp>  | 
12  |  | #endif  | 
13  |  |  | 
14  |  | #include <chrono>  | 
15  |  | #include <functional>  | 
16  |  | #include <map>  | 
17  |  | #include <vector>  | 
18  |  |  | 
19  |  | #include "crow/logging.h"  | 
20  |  |  | 
21  |  | namespace crow  | 
22  |  | { | 
23  |  | #ifdef CROW_USE_BOOST  | 
24  |  |     namespace asio = boost::asio;  | 
25  |  |     using error_code = boost::system::error_code;  | 
26  |  | #else  | 
27  |  |     using error_code = asio::error_code;  | 
28  |  | #endif  | 
29  |  |     namespace detail  | 
30  |  |     { | 
31  |  |  | 
32  |  |         /// A class for scheduling functions to be called after a specific  | 
33  |  |         /// amount of ticks. Ther tick length can  be handed over in constructor,   | 
34  |  |         /// the default tick length is equal to 1 second.  | 
35  |  |         class task_timer  | 
36  |  |         { | 
37  |  |         public:  | 
38  |  |             using task_type = std::function<void()>;  | 
39  |  |             using identifier_type = size_t;  | 
40  |  |  | 
41  |  |         private:  | 
42  |  |             using clock_type = std::chrono::steady_clock;  | 
43  |  |             using time_type = clock_type::time_point;  | 
44  |  |         public:  | 
45  |  |             task_timer(asio::io_context& io_context,  | 
46  |  |                        const std::chrono::milliseconds tick_length =  | 
47  |  |                             std::chrono::seconds(1)) :  | 
48  |  |               io_context_(io_context), timer_(io_context_),  | 
49  |  |               tick_length_ms_(tick_length)  | 
50  | 0  |             { | 
51  | 0  |                 timer_.expires_after(tick_length_ms_);  | 
52  | 0  |                 timer_.async_wait(  | 
53  | 0  |                   std::bind(&task_timer::tick_handler, this,  | 
54  | 0  |                   std::placeholders::_1));  | 
55  | 0  |             }  | 
56  |  |  | 
57  | 0  |             ~task_timer() { timer_.cancel(); } | 
58  |  |  | 
59  |  |             /// Cancel the scheduling of the given task   | 
60  |  |             ///  | 
61  |  |             /// \param identifier_type task identifier of the task to cancel.  | 
62  |  |             void cancel(identifier_type id)  | 
63  | 0  |             { | 
64  | 0  |                 tasks_.erase(id);  | 
65  | 0  |                 CROW_LOG_DEBUG << "task_timer task cancelled: " << this << ' ' << id;  | 
66  | 0  |             }  | 
67  |  |  | 
68  |  |             /// Schedule the given task to be executed after the default amount  | 
69  |  |             /// of ticks.  | 
70  |  |  | 
71  |  |             ///  | 
72  |  |             /// \return identifier_type Used to cancel the thread.  | 
73  |  |             /// It is not bound to this task_timer instance and in some cases  | 
74  |  |             /// could lead to undefined behavior if used with other task_timer  | 
75  |  |             /// objects or after the task has been successfully executed.  | 
76  |  |             identifier_type schedule(const task_type& task)  | 
77  | 0  |             { | 
78  | 0  |                 return schedule(task, get_default_timeout());  | 
79  | 0  |             }  | 
80  |  |  | 
81  |  |             /// Schedule the given task to be executed after the given time.  | 
82  |  |  | 
83  |  |             ///  | 
84  |  |             /// \param timeout The amount of ticks to wait before execution.  | 
85  |  |             ///  | 
86  |  |             /// \return identifier_type Used to cancel the thread.  | 
87  |  |             /// It is not bound to this task_timer instance and in some cases  | 
88  |  |             /// could lead to undefined behavior if used with other task_timer  | 
89  |  |             /// objects or after the task has been successfully executed.  | 
90  |  |             identifier_type schedule(const task_type& task, uint8_t timeout)  | 
91  | 0  |             { | 
92  | 0  |                 tasks_.insert({++highest_id_, | 
93  | 0  |                                {clock_type::now() + (timeout * tick_length_ms_), | 
94  | 0  |                                 task}});  | 
95  | 0  |                 CROW_LOG_DEBUG << "task_timer scheduled: " << this << ' ' <<  | 
96  | 0  |                                   highest_id_;  | 
97  | 0  |                 return highest_id_;  | 
98  | 0  |             }  | 
99  |  |  | 
100  |  |             /// Set the default timeout for this task_timer instance.  | 
101  |  |             /// (Default: 5)  | 
102  |  |  | 
103  |  |             ///  | 
104  |  |             /// \param timeout The amount of ticks to wait before  | 
105  |  |             /// execution.   | 
106  |  |             /// For tick length \see tick_length_ms_   | 
107  | 0  |             void set_default_timeout(uint8_t timeout) { | 
108  | 0  |                 default_timeout_ = timeout;  | 
109  | 0  |             }  | 
110  |  |  | 
111  |  |             /// Get the default timeout. (Default: 5)  | 
112  | 0  |             uint8_t get_default_timeout() const { | 
113  | 0  |                 return default_timeout_;  | 
114  | 0  |             }  | 
115  |  |  | 
116  |  |             /// returns the length of one tick.  | 
117  | 0  |             std::chrono::milliseconds get_tick_length() const { | 
118  | 0  |                 return tick_length_ms_;  | 
119  | 0  |             }  | 
120  |  |  | 
121  |  |         private:  | 
122  |  |             void process_tasks()  | 
123  | 0  |             { | 
124  | 0  |                 time_type current_time = clock_type::now();  | 
125  | 0  |                 std::vector<identifier_type> finished_tasks;  | 
126  | 0  | 
  | 
127  | 0  |                 for (const auto& task : tasks_)  | 
128  | 0  |                 { | 
129  | 0  |                     if (task.second.first < current_time)  | 
130  | 0  |                     { | 
131  | 0  |                         (task.second.second)();  | 
132  | 0  |                         finished_tasks.push_back(task.first);  | 
133  | 0  |                         CROW_LOG_DEBUG << "task_timer called: " << this <<  | 
134  | 0  |                                           ' ' << task.first;  | 
135  | 0  |                     }  | 
136  | 0  |                 }  | 
137  | 0  | 
  | 
138  | 0  |                 for (const auto& task : finished_tasks)  | 
139  | 0  |                     tasks_.erase(task);  | 
140  | 0  | 
  | 
141  | 0  |                 // If no task is currently scheduled, reset the issued ids back  | 
142  | 0  |                 // to 0.  | 
143  | 0  |                 if (tasks_.empty()) highest_id_ = 0;  | 
144  | 0  |             }  | 
145  |  |  | 
146  |  |             void tick_handler(const error_code& ec)  | 
147  | 0  |             { | 
148  | 0  |                 if (ec) return;  | 
149  | 0  | 
  | 
150  | 0  |                 process_tasks();  | 
151  | 0  | 
  | 
152  | 0  |                 timer_.expires_after(tick_length_ms_);  | 
153  | 0  |                 timer_.async_wait(  | 
154  | 0  |                   std::bind(&task_timer::tick_handler, this, std::placeholders::_1));  | 
155  | 0  |             }  | 
156  |  |  | 
157  |  |         private:  | 
158  |  |             asio::io_context& io_context_;  | 
159  |  |             asio::basic_waitable_timer<clock_type> timer_;  | 
160  |  |             std::map<identifier_type, std::pair<time_type, task_type>> tasks_;  | 
161  |  |  | 
162  |  |             // A continuously increasing number to be issued to threads to  | 
163  |  |             // identify them. If no tasks are scheduled, it will be reset to 0.  | 
164  |  |             identifier_type highest_id_{0}; | 
165  |  |             std::chrono::milliseconds tick_length_ms_;  | 
166  |  |             uint8_t default_timeout_{5}; | 
167  |  |  | 
168  |  |         };  | 
169  |  |     } // namespace detail  | 
170  |  | } // namespace crow  |