Coverage Report

Created: 2026-10-28 07:07

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