fix(sched): exit_current_thread 先标记 THREAD_DONE 再回收资源,堵住已退线程被再调度的窗口

原顺序是 free_user_thread 在前、state=THREAD_DONE 在后:在已释放用户栈/上下文但 state 仍为 RUNNING 的窗口里,若发生一次再调度(定时器 IRQ → try_reschedule → _yield 仅按 state 过滤),本线程会被再次选中并带着失效上下文回用户态,在已回收的栈上取指 0(现场 [0E] rip=0x0 err=0x15 cr2=0x0 rsp=0x7fffffde0000,紧跟 [syscall] exit called)。_yield 的选择逻辑(THREAD_READY/RUNNING 才可选)本身已正确,故只需把死亡标记提前。
This commit is contained in:
TermiNexus committed 2026-10-16 16:47:00 +08:00
1 parent 424233dcea
commit 3ee35ad586
1 file changed
+10 -2
+10 -2
View File
@@ -249,12 +249,20 @@ class Scheduler:
@staticmethod
def exit_current_thread():
"""用户线程显式退出(THREAD_EXIT):回收资源 → 标记 DONE → 让出,永不返回。"""
"""用户线程显式退出(THREAD_EXIT):标记 DONE → 回收资源 → 让出,永不返回。
次序很重要:**先**置 THREAD_DONE,**再** free_user_thread。反过来的话,在
"已释放用户栈/上下文、但 state 还是 RUNNING" 这段窗口里只要发生一次再调度
(定时器 IRQ → try_reschedule → _yield 的选择只按 state 过滤),本线程就会被
再次选中并带着失效上下文回到用户态 ⇒ 在已回收的栈上取指 0:
现场 [0E] rip=0x0 err=0x15(用户态取指缺页)cr2=0x0 rsp=0x7fffffde0000,tid 为该 app 线程,
紧跟在 [syscall] exit called 之后。
"""
s: Scheduler* = _sched_ptr
tid: int = s.current_tid
proc.free_user_thread(proc.find_user_slot_by_tid(tid))
if tid > 0 and tid < MAX_THREADS:
s.threads[tid].state = THREAD_DONE
proc.free_user_thread(proc.find_user_slot_by_tid(tid))
Scheduler._yield()
# _yield 可能因"只有自己可运行"而直接返回:此后绝不能再跑用户代码(栈已回收)。
while True: