/src/gbmcweb/tlbmc/scheduler/scheduler.h
Line | Count | Source |
1 | | #ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_SCHEDULER_SCHEDULER_H_ |
2 | | #define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_SCHEDULER_SCHEDULER_H_ |
3 | | |
4 | | #include <stdbool.h> |
5 | | |
6 | | #include <atomic> |
7 | | #include <cstddef> |
8 | | #include <cstdint> |
9 | | #include <memory> |
10 | | #include <optional> |
11 | | #include <thread> |
12 | | |
13 | | #include "absl/base/thread_annotations.h" |
14 | | #include "absl/container/flat_hash_map.h" |
15 | | #include "absl/functional/any_invocable.h" |
16 | | #include "absl/synchronization/mutex.h" |
17 | | #include "absl/time/time.h" |
18 | | #include "boost/asio/steady_timer.hpp" |
19 | | #include <boost/asio/executor_work_guard.hpp> |
20 | | #include "boost/circular_buffer.hpp" //NOLINT: boost is commonly used in BMC |
21 | | #include "time/clock.h" |
22 | | #include "nlohmann/json.hpp" |
23 | | |
24 | | namespace milotic_tlbmc { |
25 | | |
26 | | // Represents the time information of a task. |
27 | | struct TimeInfo { |
28 | | absl::Time last_scheduled_time = absl::InfiniteFuture(); |
29 | | absl::Time last_run_time = absl::InfiniteFuture(); |
30 | | boost::circular_buffer<absl::Duration> wait_times = |
31 | | boost::circular_buffer<absl::Duration>(1000); |
32 | | |
33 | | nlohmann::json ToJson() const; |
34 | | }; |
35 | | |
36 | | // Represents the mode of a task. |
37 | | // |
38 | | // kPeriodic: The task will be scheduled periodically. |
39 | | // kOnce: The task will be cancelled after it is executed once. |
40 | | enum class TaskMode : uint8_t { |
41 | | kPeriodic = 0, |
42 | | kOnce, |
43 | | }; |
44 | | |
45 | | // Represents a single task to be scheduled and run. |
46 | | class Task : public std::enable_shared_from_this<Task> { |
47 | | public: |
48 | | Task(int task_id, |
49 | | absl::AnyInvocable<void(absl::AnyInvocable<void()>)> task_fn, |
50 | | absl::Duration period, boost::asio::io_context* io, |
51 | | absl::Duration task_timeout, TaskMode task_mode = TaskMode::kPeriodic, |
52 | | ecclesia::Clock* clock = ecclesia::Clock::RealClock()); |
53 | | |
54 | | void Run() ABSL_LOCKS_EXCLUDED(task_mutex_, time_info_mutex_); |
55 | | void ScheduleNextRun() ABSL_LOCKS_EXCLUDED(timer_mutex_); |
56 | | absl::Duration GetPeriod() const; |
57 | | void SetPeriod(absl::Duration period); |
58 | | void Cancel() ABSL_LOCKS_EXCLUDED(timer_mutex_); |
59 | | void OnDone() ABSL_LOCKS_EXCLUDED(timer_mutex_, time_info_mutex_); |
60 | 0 | bool IsCancelled() const { return cancelled_.load(); } |
61 | 0 | int GetExecutionCount() const { return execution_count_.load(); } |
62 | | absl::Time GetLastRunTime() const ABSL_LOCKS_EXCLUDED(time_info_mutex_); |
63 | | absl::Duration GetAverageWaitTime() const |
64 | | ABSL_LOCKS_EXCLUDED(time_info_mutex_); |
65 | 0 | absl::Duration GetTaskTimeout() const { return task_timeout_; } |
66 | | nlohmann::json ToJson() const; |
67 | | |
68 | | private: |
69 | | const int task_id_; |
70 | | const TaskMode task_mode_; |
71 | | // Ecclesia clock is thread safe. |
72 | | ecclesia::Clock* clock_ = nullptr; |
73 | | std::atomic<absl::Duration> period_ = absl::InfiniteDuration(); |
74 | | std::atomic<bool> cancelled_ = false; |
75 | | std::atomic<int> execution_count_ = 0; |
76 | | std::atomic<absl::Duration> last_execution_duration_ = absl::ZeroDuration(); |
77 | | |
78 | | // Task function to be executed. |
79 | | // task_fn_with_ack_: This is used for async tasks i.e tasks with |
80 | | // TaskMode kPeriodic that have an ack callback. |
81 | | absl::AnyInvocable<void(absl::AnyInvocable<void()>)> task_fn_with_ack_ |
82 | | ABSL_GUARDED_BY(task_mutex_) = nullptr; |
83 | | mutable absl::Mutex task_mutex_; |
84 | | |
85 | | // TimeInfo to capture the last, next and average wait time. |
86 | | TimeInfo time_info_ ABSL_GUARDED_BY(time_info_mutex_); |
87 | | mutable absl::Mutex time_info_mutex_; |
88 | | |
89 | | // Timer to schedule the task. |
90 | | // Mainly used for async tasks. |
91 | | std::unique_ptr<boost::asio::steady_timer> timer_ |
92 | | ABSL_GUARDED_BY(timer_mutex_) = nullptr; |
93 | | absl::Mutex timer_mutex_; |
94 | | const absl::Duration task_timeout_; |
95 | | }; |
96 | | |
97 | | // Schedules tasks with a given periodicity. |
98 | | // |
99 | | // The scheduler is responsible for creating a thread for each period and |
100 | | // scheduling the tasks to run on the corresponding thread based on the period. |
101 | | // The scheduler also provides a way to reschedule a task with a new period and |
102 | | // cancel a task. |
103 | | // |
104 | | // The scheduler is thread safe. |
105 | | class TaskScheduler { |
106 | | public: |
107 | | // Options to configure the scheduler. |
108 | | struct Options { |
109 | | // Default options for the scheduler. |
110 | | // |
111 | | // Cleanup tasks period is set to 10 seconds arbitrarily, not shorter to |
112 | | // prevent overloading scheduler for cleanup tasks. |
113 | | // |
114 | | // Default task timeout is set to 60 seconds to allow for long running |
115 | | // tasks to complete which today aligns with the default timeout for |
116 | | // Redfish requests on client side. |
117 | | Options() |
118 | 2.04k | : cleanup_tasks_period(absl::Seconds(10)), |
119 | 2.04k | default_task_timeout(absl::Seconds(60)) {} |
120 | | |
121 | | absl::Duration cleanup_tasks_period; |
122 | | absl::Duration default_task_timeout; |
123 | | }; |
124 | | |
125 | | explicit TaskScheduler(ecclesia::Clock* clock = ecclesia::Clock::RealClock(), |
126 | | const Options& options = Options()); |
127 | | virtual ~TaskScheduler(); |
128 | | // Schedules the `task` for async invocation with the given `period`. |
129 | | // |
130 | | // The `task` will be invoked with a `on_done` callback as a parameter. |
131 | | // The `on_done` callback will be invoked when the task is done. |
132 | | // The `timeout` parameter is the timeout for the task to complete. If not |
133 | | // specified, the infinite timeout is used. If a task is not completed within |
134 | | // the timeout, the task will be cancelled. |
135 | | int RunAndScheduleAsync( |
136 | | absl::AnyInvocable<void(absl::AnyInvocable<void()>)> task, |
137 | | absl::Duration period, |
138 | | std::optional<absl::Duration> timeout = absl::InfiniteDuration()); |
139 | | |
140 | | // Same as RunAndScheduleAsync but does not run the task immediately before |
141 | | // scheduling it. |
142 | | int ScheduleAsync( |
143 | | absl::AnyInvocable<void(absl::AnyInvocable<void()>)> task, |
144 | | absl::Duration period, |
145 | | std::optional<absl::Duration> timeout = absl::InfiniteDuration()); |
146 | | |
147 | | // Schedule a oneshot task that is async. |
148 | | int ScheduleOneShotAsync( |
149 | | absl::AnyInvocable<void(absl::AnyInvocable<void()>)> task, |
150 | | absl::Duration period, |
151 | | std::optional<absl::Duration> timeout = std::nullopt); |
152 | | |
153 | | void UpdateTaskPeriod(int task_id, absl::Duration period) |
154 | | ABSL_LOCKS_EXCLUDED(async_tasks_mutex_); |
155 | | void Cancel(int task_id) ABSL_LOCKS_EXCLUDED(async_tasks_mutex_); |
156 | | void CancelAll() ABSL_LOCKS_EXCLUDED(async_tasks_mutex_); |
157 | | void Stop() ABSL_LOCKS_EXCLUDED(async_tasks_mutex_); |
158 | | size_t GetSchedulingAccuracyPercentage() const; |
159 | | nlohmann::json ToJson() const; |
160 | | |
161 | | // This only counts the number of user scheduled async tasks. |
162 | | size_t GetAllUserTaskCount() const ABSL_LOCKS_EXCLUDED(async_tasks_mutex_); |
163 | | // This counts the number of all tasks including the internal scheduler tasks. |
164 | | size_t GetAllTaskCount() const ABSL_LOCKS_EXCLUDED(async_tasks_mutex_); |
165 | | |
166 | | protected: |
167 | | virtual absl::flat_hash_map<int, std::shared_ptr<Task>> GetSnapshot() const |
168 | | ABSL_LOCKS_EXCLUDED(async_tasks_mutex_); |
169 | | |
170 | | void CleanupInactiveTasks() ABSL_LOCKS_EXCLUDED(async_tasks_mutex_); |
171 | | |
172 | | private: |
173 | | enum class ExecutionMode : uint8_t { |
174 | | kRunImmediately = 0, |
175 | | kRunAfterScheduling, |
176 | | }; |
177 | | |
178 | | int ScheduleAsyncInternal( |
179 | | absl::AnyInvocable<void(absl::AnyInvocable<void()>)> task, |
180 | | absl::Duration period, std::optional<absl::Duration> timeout, |
181 | | ExecutionMode execution_mode); |
182 | | |
183 | | std::atomic<int> next_task_id_; |
184 | | mutable absl::Mutex async_tasks_mutex_; |
185 | | absl::flat_hash_map<int, std::shared_ptr<Task>> id_to_async_tasks_ |
186 | | ABSL_GUARDED_BY(async_tasks_mutex_); |
187 | | ecclesia::Clock* clock_; |
188 | | std::unique_ptr<boost::asio::io_context> io_; |
189 | | boost::asio::executor_work_guard<boost::asio::io_context::executor_type> |
190 | | work_guard_; |
191 | | std::thread async_executor_thread_; |
192 | | std::atomic<bool> stop_ = false; |
193 | | std::atomic<int> cleanup_task_id_ = -1; |
194 | | absl::Duration default_task_timeout_; |
195 | | }; |
196 | | |
197 | | } // namespace milotic_tlbmc |
198 | | |
199 | | #endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_SCHEDULER_SCHEDULER_H_ |