@@ -33,41 +33,43 @@ using TimerDelegate = ReportScheduler::TimerDelegate;
33
33
/* *
34
34
* @class Synchronized ReportSchedulerImpl
35
35
*
36
- * @brief This class extends ReportSchedulerImpl and overrides it's scheduling logic.
36
+ * @brief This class extends ReportSchedulerImpl and overrides its scheduling logic.
37
37
*
38
- * It only overrides Observers method where the scheduling logic make it necessary, the others are kept as is .
38
+ * It overrides the OnTransitionToIdle methods from ReadHandler::Observer .
39
39
*
40
- * It inherits from TimerContext so that it can be used as a TimerDelegate instead on relying on the nodes to schedule themselves.
40
+ * It inherits from TimerContext so that it can be used as a TimerDelegate instead of relying on the nodes to schedule themselves.
41
41
*
42
42
* ## Scheduling Logic
43
43
*
44
44
* This class implements a scheduling logic that aims to make all ReadHandlers report at the same time when possible.
45
- * The goal is to minimize the different times a device wakes up to report, and thus this aims to schedule all reports at the latest
45
+ * The goal is to minimize the number of times a device wakes up to report, and thus this aims to schedule all reports at the latest
46
46
* possible time while ensuring that all reports get sent before their max interval.
47
47
*
48
- * The logic also aims to minimize the impact on the responsivity of the device.
48
+ * The logic also aims to minimize the impact on the responsiveness of the device.
49
49
*
50
50
* The scheduling logic is as follows:
51
- * - The CalculateNextReportTimeout is called by the same ReadHandler Observer callbacks than the non-synchronized implementation:
51
+ * - The CalculateNextReportTimeout is called by any ReadHandler methods that affect when/whether a report should be sent. These
52
+ * are:
52
53
* * OnSubscriptionEstablished,
53
54
* * OnBecameReportable,
54
55
* * OnSubscriptionReportSent
55
56
*
56
- * - The Synchronized Scheduler keeps track of the next min and max interval timestamps. It updates in CalculateNextReportTimeout
57
+ * - The Synchronized Scheduler keeps track of the next min and max interval timestamps and updates them in
58
+ * CalculateNextReportTimeout
57
59
*
58
60
* - The next max interval is calculated as the earliest max interval of all the registered ReadHandlersNodes.
59
61
*
60
62
* - The next min interval is calculated as the latest min interval of the registered ReadHandlersNodes that:
61
63
* * Have a min timestamp greater than the current time
62
- * * Are Reportable (this prevents a ReadHandler that is not reportable to hold the report of all the others )
64
+ * * Are Reportable (this prevents a ReadHandler that is not reportable from blocking the reporting of other ReadHandlers )
63
65
* TODO: Assess if we want to keep this behavior or simply let the min interval be the earliest min interval to prevent cases
64
66
* where a ReadHandler with a dirty path but a very high min interval blocks all reports
65
- * - If no ReadHandlerNode matches min interval the criteria, the next min interval is set to current timestamp.
67
+ * - If no ReadHandlerNode matches the min interval criteria, the next min interval is set to the current timestamp.
66
68
*
67
69
* - The next report timeout is calculated in CalculatedNextReportTimeout based on the next min and max interval timestamps, as well
68
70
* as the status of each ReadHandlerNode in the pool.
69
71
*
70
- * @note Unlike the non-synchronized implementation, the Synchronized Scheduler will reschedule itself in the event where a timer
72
+ * @note Unlike the non-synchronized implementation, the Synchronized Scheduler will reschedule itself in the event that a timer
71
73
* fires before a reportable timestamp is reached.
72
74
*
73
75
* @note In this implementation, nodes still keep track of their own min and max interval timestamps.
@@ -86,15 +88,15 @@ class SynchronizedReportSchedulerImpl : public ReportSchedulerImpl, public Timer
86
88
87
89
/* * @brief Callback called when the report timer expires to schedule an engine run regardless of the state of the ReadHandlers,
88
90
*
89
- * It loops through all handlers and sets their CanBeSynced flag to true if the current timstamp is greater than
91
+ * It loops through all handlers and sets their CanBeSynced flag to true if the current timestamp is greater than
90
92
* their respective minimal timestamps.
91
93
*
92
94
* While looping, it checks if any handler is reportable now. If not, we recalculate the next report timeout and reschedule the
93
95
* report.
94
96
*
95
- * If a Readhangler is reportable now, an engine run is scheduled.
97
+ * If a Readhandler is reportable now, an engine run is scheduled.
96
98
*
97
- * If the timer expires after all nodes were unregistered, no action is taken.
99
+ * If the timer expires after all nodes are unregistered, no action is taken.
98
100
*/
99
101
void TimerFired () override ;
100
102
@@ -104,11 +106,11 @@ class SynchronizedReportSchedulerImpl : public ReportSchedulerImpl, public Timer
104
106
*
105
107
* If a report is already scheduled, cancel it and schedule a new one.
106
108
*
107
- * @param[in] timeout The timeout to schedule the report.
108
- * @param[in] node The node associated to the ReadHandler.
109
+ * @param[in] timeout The delay before the report will happen .
110
+ * @param[in] node The node associated with the ReadHandler.
109
111
* @param[in] now The current system timestamp.
110
112
*
111
- * @return CHIP_ERROR CHIP_NO_ERROR on success, timer related error code otherwise (This can only fail on starting the timer)
113
+ * @return CHIP_ERROR CHIP_NO_ERROR on success, timer- related error code otherwise (This can only fail on starting the timer)
112
114
*/
113
115
CHIP_ERROR ScheduleReport (System::Clock::Timeout timeout, ReadHandlerNode * node, const Timestamp & now) override ;
114
116
void CancelReport ();
@@ -119,7 +121,8 @@ class SynchronizedReportSchedulerImpl : public ReportSchedulerImpl, public Timer
119
121
/* *
120
122
* @brief Find the highest minimum timestamp possible that still respects the lowest max timestamp and sets it as the common
121
123
* minimum. If the max timestamp has not been updated and is in the past, or if no min timestamp is lower than the current max
122
- * timestamp, this will set now as the common minimum timestamp, thus allowing the report to be sent immediately.
124
+ * timestamp, this will set the "now" parameter as the common minimum timestamp, thus allowing the report to be sent
125
+ * immediately.
123
126
*
124
127
* @param[in] now The current system timestamp, set by the event that triggered the call of this method.
125
128
*
@@ -150,13 +153,23 @@ class SynchronizedReportSchedulerImpl : public ReportSchedulerImpl, public Timer
150
153
* and the current time.
151
154
* * If no ReadHandlerNode is reportable, the timeout is set to the difference between the Scheduler's max timestamp and the
152
155
* current time.
156
+ @note When looping through the ReadHandlerNodes, the IsEngineRunScheduled flag is used to prevent calling ScheduleRun on a
157
+ ReadHandler that already has an engine run scheduled, which would cause an endless report loop in some cases. The only
158
+ reason why we would want to call ScheduleRun on a node that already has an engine run scheduled is if the ongoing report
159
+ is chunked, which means that the report is not fully sent yet and that the EngineRun should be scheduled again until
160
+ there are no chunks left.
161
+
162
+ The Endless Reporting Loop Scenario would be:
163
+ 1. At least two ReadHandlers are registered to the Scheduler
164
+ 2. ScheduleRun() is called with 2 reportable ReadHandlers (meaning they both return true to IsReportableNow())
165
+ 3. The Scheduler sends the first report and calls OnSubscriptionReportSent on the ReadHandler
166
+ 4. OnSubscriptionReportSent calls CalculateNextReportTimeout, which loops through all ReadHandlers and finds that a least
167
+ one ReadHandler is reportable now, and thus sets the timeout to 0.
168
+ 5. OnSubscriptionReportSent then calls ScheduleReport with a timeout of 0, which calls TimerFired on the Scheduler
169
+ 6. If the MinInterval of the ReadHandler is 0, the Scheduler will set the CanBeSynced flag to true, and the
170
+ IsReportableNow Will return true since (now >= MinTimestamp || CanBeSynced()) will be true.
171
+ 7. ScheduleRun() will be called on the ReadHandler with 2 reportable ReadHandlers, and the loop will start again.
153
172
*
154
- * @note Since this method is called after the OnSubscriptionReportSent callback, to avoid an endless reporting loop, Nodes with
155
- * the IsEngineRunScheduled flag set are ignored when finding if the Scheduler should report at min, max or now.
156
- *
157
- * @note If a ReadHandler's report is Chunked, the IsEngineRunScheduled is ignored since we do want to keep rescheduling the
158
- * report to the now timestamp until it is fully sent. IsChunkedReport is used to prevent starting a chunked report and
159
- * then waiting on the max interval after the first chunk is sent.
160
173
*/
161
174
CHIP_ERROR CalculateNextReportTimeout (Timeout & timeout, ReadHandlerNode * aReadHandlerNode, const Timestamp & now) override ;
162
175
0 commit comments