Coverage Report

Created: 2025-10-28 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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