hujun260 commented on PR #19398:
URL: https://github.com/apache/nuttx/pull/19398#issuecomment-4953320919

   
   
   
   
   > @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`.
   
   @94xhn 
   The case you just described, where this_task and g_running_tasks can be 
unequal, only occurs in an interrupt context. However:
   1 nxtask_exit runs in a thread context. A thread can never preempt an 
interrupt; once the interrupt exits and thread execution resumes, this_task and 
g_running_tasks will be equal again.
   2 nxtask_exit runs with interrupts masked.


-- 
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]

Reply via email to