|
19 | 19 | #include <stdint.h> |
20 | 20 | #include <string.h> |
21 | 21 |
|
| 22 | +#if defined(SITL_BUILD) |
| 23 | +#include <unistd.h> |
| 24 | +#endif |
| 25 | + |
22 | 26 | #include "platform.h" |
23 | 27 |
|
24 | 28 | #include "scheduler.h" |
@@ -215,11 +219,22 @@ void FAST_CODE NOINLINE scheduler(void) |
215 | 219 | uint16_t selectedTaskDynamicPriority = 0; |
216 | 220 | bool forcedRealTimeTask = false; |
217 | 221 |
|
| 222 | +#if defined(SITL_BUILD) |
| 223 | + // Track the earliest time at which the next task will become due so we can |
| 224 | + // sleep until then instead of busy-waiting. Cap at 1 ms so event-driven |
| 225 | + // tasks (checkFunc) are still polled frequently enough. |
| 226 | + timeUs_t sitlEarliestNextTaskAt = currentTimeUs + 1000; |
| 227 | + bool sitlHasCheckFuncTask = false; |
| 228 | +#endif |
| 229 | + |
218 | 230 | // Update task dynamic priorities |
219 | 231 | uint16_t waitingTasks = 0; |
220 | 232 | for (cfTask_t *task = queueFirst(); task != NULL; task = queueNext()) { |
221 | 233 | // Task has checkFunc - event driven |
222 | 234 | if (task->checkFunc) { |
| 235 | +#if defined(SITL_BUILD) |
| 236 | + sitlHasCheckFuncTask = true; |
| 237 | +#endif |
223 | 238 | const timeUs_t currentTimeBeforeCheckFuncCallUs = micros(); |
224 | 239 |
|
225 | 240 | // Increase priority for event driven tasks |
@@ -248,7 +263,19 @@ void FAST_CODE NOINLINE scheduler(void) |
248 | 263 | waitingTasks++; |
249 | 264 | forcedRealTimeTask = true; |
250 | 265 | } |
| 266 | +#if defined(SITL_BUILD) |
| 267 | + const timeUs_t taskNextAt = task->lastExecutedAt + (timeUs_t)task->desiredPeriod; |
| 268 | + if (taskNextAt < sitlEarliestNextTaskAt) { |
| 269 | + sitlEarliestNextTaskAt = taskNextAt; |
| 270 | + } |
| 271 | +#endif |
251 | 272 | } else { |
| 273 | +#if defined(SITL_BUILD) |
| 274 | + const timeUs_t taskNextAt = task->lastExecutedAt + (timeUs_t)task->desiredPeriod; |
| 275 | + if (taskNextAt < sitlEarliestNextTaskAt) { |
| 276 | + sitlEarliestNextTaskAt = taskNextAt; |
| 277 | + } |
| 278 | +#endif |
252 | 279 | // Task is time-driven, dynamicPriority is last execution age (measured in desiredPeriods) |
253 | 280 | // Task age is calculated from last execution |
254 | 281 | task->taskAgeCycles = ((timeDelta_t)(currentTimeUs - task->lastExecutedAt)) / task->desiredPeriod; |
@@ -294,4 +321,27 @@ void FAST_CODE NOINLINE scheduler(void) |
294 | 321 | selectedTask->totalExecutionTime += taskExecutionTime; // time consumed by scheduler + task |
295 | 322 | selectedTask->maxExecutionTime = MAX(selectedTask->maxExecutionTime, taskExecutionTime); |
296 | 323 | } |
| 324 | + |
| 325 | +#if defined(SITL_BUILD) |
| 326 | + { |
| 327 | + // Avoid busy-waiting and burning 100% CPU in SITL. After executing the |
| 328 | + // current task (or finding nothing to do), sleep until just before the |
| 329 | + // next task is due. For event-driven tasks (checkFunc) we limit the |
| 330 | + // sleep so the check function is still called frequently. |
| 331 | + if (sitlHasCheckFuncTask) { |
| 332 | + // Poll event-driven tasks at least every 500 µs |
| 333 | + const timeUs_t eventCap = micros() + 500; |
| 334 | + if (eventCap < sitlEarliestNextTaskAt) { |
| 335 | + sitlEarliestNextTaskAt = eventCap; |
| 336 | + } |
| 337 | + } |
| 338 | + const timeUs_t nowUs = micros(); |
| 339 | + if (sitlEarliestNextTaskAt > nowUs + 50) { |
| 340 | + const timeDelta_t sleepUs = (timeDelta_t)(sitlEarliestNextTaskAt - nowUs) - 50; |
| 341 | + if (sleepUs > 0) { |
| 342 | + usleep((useconds_t)sleepUs); |
| 343 | + } |
| 344 | + } |
| 345 | + } |
| 346 | +#endif |
297 | 347 | } |
0 commit comments