@@ -104,6 +104,22 @@ struct scheduled_context
104104 std::reference_wrapper<context> ctx;
105105 time_point wake_time = time_point::max();
106106
107+ /* *
108+ * @brief Construct a new scheduled context object.
109+ *
110+ * Initializes a scheduler entry by registering the unblock listener on the
111+ * context and computing the initial wake deadline based on the context's
112+ * current blocking state. If the context is time-blocked, the wake deadline
113+ * is set to the current time plus the context's sleep duration. Otherwise,
114+ * the wake deadline is set to time_point::max() (never).
115+ *
116+ * @param p_ctx The execution context to manage in the scheduler.
117+ * @param p_listener Unblock listener to register on the context for
118+ * notification when the context becomes ready to resume. May be nullptr if
119+ * event-driven wakeup is not needed.
120+ * @param p_clock Clock instance used to compute wake deadlines when
121+ * the context is time-blocked.
122+ */
107123 scheduled_context (async::context& p_ctx,
108124 async::unblock_listener* p_listener,
109125 Clock const & p_clock)
@@ -113,20 +129,19 @@ struct scheduled_context
113129 refresh (p_clock);
114130 }
115131
116- // Bind this entry to p_ctx, register p_listener (may be nullptr), and
117- // compute the initial wake_time. Must be called exactly once before any
118- // other method.
119- void assign (async::context& p_ctx,
120- async::unblock_listener* p_listener,
121- Clock const & p_clock)
122- {
123- ctx = p_ctx;
124- p_ctx.on_unblock (p_listener);
125- refresh (p_clock);
126- }
127-
128- // Recompute wake_time from the context's current sleep_time. Called after
129- // every resume because the blocking state may have changed.
132+ /* *
133+ * @brief Recompute wake_time from the context's current blocking state.
134+ *
135+ * Updates the wake deadline based on the context's current blocking state.
136+ * If the context is time-blocked, computes an absolute deadline by adding
137+ * the context's sleep duration to the current time. Otherwise, sets the
138+ * deadline to time_point::max() (never wake).
139+ *
140+ * Called after every context resume because the blocking state may have
141+ * changed.
142+ *
143+ * @param p_clock Clock instance used to compute absolute wake times.
144+ */
130145 void refresh (Clock const & p_clock)
131146 {
132147 if (ctx.get ().state () == blocked_by::time) {
@@ -137,42 +152,75 @@ struct scheduled_context
137152 }
138153
139154 /* *
140- * @brief Resume the context if its deadline has elapsed or it is otherwise
141- * ready, then refresh wake_time to reflect the new state.
155+ * @brief Recompute wake_time and update the soonest wake deadline.
142156 *
143- * @param p_clock - clock to get the current time from
144- * @return true - context was resumed
145- * @return false - context is done
157+ * Refreshes the wake deadline from the context's current blocking state,
158+ * then updates the global soonest wake deadline if this entry's deadline
159+ * is earlier.
160+ *
161+ * Called after every context resume because the blocking state may have
162+ * changed.
163+ *
164+ * @param p_clock Clock instance used to compute absolute wake times.
165+ * @param p_soonest_time Reference to the global soonest wake deadline,
166+ * updated if this entry's deadline is earlier.
146167 */
147- void resume (Clock const & p_clock)
168+ void refresh (Clock const & p_clock, time_point& p_soonest_time )
148169 {
149- if (ctx.get ().state () == blocked_by::time && wake_time <= p_clock.now ()) {
170+ refresh (p_clock);
171+
172+ if (wake_time < p_soonest_time) {
173+ p_soonest_time = wake_time;
174+ }
175+ }
176+
177+ /* *
178+ * @brief Resume the context if its deadline has elapsed or it is ready,
179+ * then refresh wake_time to reflect the new state.
180+ *
181+ * If the context is time-blocked and its deadline has elapsed, unblocks and
182+ * resumes the context. Otherwise, if the context is ready (not blocked by
183+ * time or I/O), resumes it immediately. After resuming, refreshes the wake
184+ * deadline and updates the global soonest wake deadline.
185+ *
186+ * @param p_clock Clock instance used to get the current time and compute
187+ * wake deadlines.
188+ * @param p_soonest_time Reference to the global soonest wake deadline,
189+ * updated if this entry's deadline is earlier.
190+ */
191+ void resume (Clock const & p_clock, time_point& p_soonest_time)
192+ {
193+ if (ctx.get ().state () == blocked_by::time and wake_time <= p_clock.now ()) {
150194 // Deadline elapsed — unblock without triggering scheduler notification,
151195 // since we are the scheduler, then resume.
196+ // We skip calling the unblock listener because this scheduler
152197 ctx.get ().unblock_without_notification ();
153198 ctx.get ().resume ();
154199 // Recompute after resume — state may have changed.
155- refresh (p_clock);
200+ refresh (p_clock, p_soonest_time );
156201 } else if (is_ready ()) {
157202 ctx.get ().resume ();
158203 // Recompute after resume — state may have changed.
159- refresh (p_clock);
204+ refresh (p_clock, p_soonest_time );
160205 }
161206 }
162207
163- // Returns true if the context is not blocked by time or I/O and can be
164- // resumed immediately.
208+ /* *
209+ * @brief Check if the context is ready to resume immediately.
210+ *
211+ * Returns true if the context is not blocked by time or I/O, indicating
212+ * it can be resumed without waiting for a deadline to elapse or for an
213+ * external I/O event to complete.
214+ *
215+ * @return true if the context is not blocked by time or I/O.
216+ * @return false if the context is blocked by time or I/O.
217+ */
165218 [[nodiscard]] bool is_ready () const noexcept
166219 {
167220 return ctx.get ().state () != blocked_by::io &&
168221 ctx.get ().state () != blocked_by::time;
169222 }
170223
171- [[nodiscard]] bool operator <(time_point const & p_other) const noexcept
172- {
173- return wake_time < p_other;
174- }
175-
176224 // Clear the unblock listener so the context does not hold a pointer into
177225 // this stack frame after run_until_done_impl returns.
178226 ~scheduled_context ()
@@ -224,30 +272,32 @@ void run_until_done_impl(
224272 bool can_sleep = true ;
225273
226274 for (auto & task : tasks) {
275+ // Skip performing any of the following steps if the context is done
227276 if (task.ctx .get ().done ()) {
228277 continue ;
229278 }
230279
231280 all_done = false ;
232281
233- task.resume (p_clock);
234-
235- // After resuming, re-evaluate whether we can sleep and what the
236- // soonest deadline is
237- if (task.wake_time < soonest_wake_time) {
238- soonest_wake_time = task.wake_time ;
239- }
282+ // This resumes the context if it is elidible to be resumed. It is
283+ // elidible if it's wake time has expired or its in a pollable blocked by
284+ // state. This function also updates the `soonest_wake_time` variable if
285+ // this task could be awaken sooner than the previous.
286+ task.resume (p_clock, soonest_wake_time);
240287
288+ // Before we move on to the next task, check if the task was blocked by
289+ // something.
241290 if (task.is_ready ()) {
242291 can_sleep = false ;
243292 }
244293 }
245294
295+ // No more work needs to be done, this is how the scheduler exits.
246296 if (all_done) {
247- break ;
297+ return ;
248298 }
249299
250- if (can_sleep && soonest_wake_time < max_wake) {
300+ if (can_sleep and soonest_wake_time < max_wake) {
251301 p_sleep_until (soonest_wake_time);
252302 }
253303 }
0 commit comments