94xhn commented on PR #19398:
URL: https://github.com/apache/nuttx/pull/19398#issuecomment-4951807989
@hujun260 Good question — I re-verified this against master rather than just
repeating the issue text. The divergence happens in `nxsched_add_readytorun()`
(`sched/sched/sched_addreadytorun.c`, non-SMP path):
```c
else if (nxsched_add_prioritized(btcb, list_readytorun()))
{
/* The new btcb was added at the head of the ready-to-run list. It
* is now the new active task!
*/
btcb->task_state = TSTATE_TASK_RUNNING;
btcb->flink->task_state = TSTATE_TASK_READYTORUN;
up_update_task(btcb);
ret = true;
}
```
When a higher-priority task `btcb` becomes ready and preemption is *not*
locked, this immediately (synchronously, in whatever context called
`nxsched_add_readytorun()`) makes `btcb` the new ready-to-run head — so
`this_task()` returns `btcb` from this point on. `up_update_task(btcb)` only
*signals* that a context switch is needed; the actual CPU-level register switch
(and the point where `g_running_tasks[cpu]` gets updated to `btcb`, e.g. in
`arm_doirq.c`/`riscv_doirq.c`) happens later, on whatever deferred mechanism
the architecture uses (e.g. PendSV on ARMv7-M). Until that later switch
actually executes, `g_running_tasks[cpu]` still correctly points at the *old*
task, which is still the one whose registers are loaded and which keeps
executing.
So the two diverge whenever a task's own currently-executing code creates or
wakes a higher-priority task partway through a call chain, without immediately
yielding the CPU. `nxtask_exit()` is reached through exactly such a chain:
`exit()` -> `_exit()` -> `nxtask_exithook()` -> `up_exit()` -> `nxtask_exit()`.
For a `CONFIG_BINFMT_LOADABLE` app this is a comparatively long, preemptible
window (`binfmt_exit()` -> `unload_module()` -> `elf_unloadbinary()` ->
`libelf_uninit()` -> `up_textheap_free()`, etc.) - if a higher-priority task is
promoted to the ready-to-run head anywhere in that window, `this_task()` in the
subsequent `nxtask_exit()` call resolves to that new task while the exiting
task (still `g_running_tasks[cpu]`) is the one that's actually finishing up.
This matches the original issue reporter's on-target finding (#19308): they
captured this exact divergence with an SRAM breadcrumb during the teardown of a
loadable ELF app - `this_task()` resolved to a live, higher-priority Wi-Fi
thread while `g_running_tasks[cpu]` correctly still pointed at the exiting app
- and reproduced the resulting hard fault deterministically with
`CONFIG_MM_FILL_ALLOCATIONS`.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]