@@ -74,16 +74,10 @@ impl Monitor {
7474 #[ cfg( unix) ]
7575 extern "C" fn sigurg_handler ( _: libc:: c_int ) {
7676 if let Ok ( mut set) = SigSet :: thread_get_mask ( ) {
77- //删除对SIGURG信号的屏蔽,使信号处理函数即使在处理中,也可以再次进入信号处理函数
78- set. remove ( Signal :: SIGURG ) ;
79- set. thread_set_mask ( )
80- . expect ( "Failed to remove SIGURG signal mask!" ) ;
81- //不抢占处于Syscall状态的协程。
82- //MonitorListener的设计理念是不对Syscall状态的协程发送信号。
77+ //MonitorListener的设计理念是只对Running状态的协程发送信号。
8378 //但由于NOTIFY_NODE移除和monitor线程遍历之间存在竞态条件,
8479 //SIGURG可能在协程刚进入Syscall状态时到达。
85- //如果此时抢占,协程会被放入syscall_map但无人唤醒(因为没有io_uring/epoll注册),
86- //导致死锁。
80+ //如果此时抢占,协程会被放入syscall_map但无人唤醒(因为没有io_uring/epoll注册), 导致死锁。
8781 // Skip preemption for coroutines in Syscall state.
8882 // MonitorListener's design is to NOT send signals to Syscall-state
8983 // coroutines. However, a race between NOTIFY_NODE removal and the
@@ -92,10 +86,14 @@ impl Monitor {
9286 // coroutine lands in the syscall map with no io_uring/epoll/timer
9387 // registration to wake it, causing a deadlock.
9488 if let Some ( co) = SchedulableCoroutine :: current ( ) {
95- if matches ! ( co. state( ) , CoroutineState :: Syscall ( ( ) , _ , _ ) ) {
89+ if ! matches ! ( co. state( ) , CoroutineState :: Running ) {
9690 return ;
9791 }
9892 }
93+ //删除对SIGURG信号的屏蔽,使信号处理函数即使在处理中,也可以再次进入信号处理函数
94+ set. remove ( Signal :: SIGURG ) ;
95+ set. thread_set_mask ( )
96+ . expect ( "Failed to remove SIGURG signal mask!" ) ;
9997 if let Some ( suspender) = SchedulableSuspender :: current ( ) {
10098 suspender. suspend ( ) ;
10199 }
@@ -530,58 +528,27 @@ std::arch::global_asm!(
530528 "ret" ,
531529) ;
532530
533- // Thread-local flag for two-level preemption on Windows.
534- // Level 1: SuspendThread fires, do_preempt sets this flag and returns
535- // without switching coroutines — the thread continues executing
536- // and exits any critical section (heap allocation, IO, etc.).
537- // If it reaches a hooked syscall, the Nio/Iocp layer will call
538- // Suspender::suspend_with cooperatively.
539- // Level 2: If the flag is still set on the next SuspendThread (~1ms later),
540- // the coroutine is truly CPU-bound with no syscalls — do_preempt
541- // forces an immediate context switch.
542- #[ cfg( windows) ]
543- thread_local ! {
544- static PREEMPT_PENDING : Cell <bool > = const { Cell :: new( false ) } ;
545- }
546-
547531#[ cfg( windows) ]
548532extern "C" fn do_preempt ( ) {
549- PREEMPT_PENDING . with ( |flag| {
550- if flag. get ( ) {
551- // Flag was already set from a previous SuspendThread attempt but the
552- // coroutine never yielded (no hooked syscalls) — it is truly CPU-bound.
553- // Force immediate suspension.
554- flag. set ( false ) ;
555- //不抢占处于Syscall状态的协程。
556- //MonitorListener的设计理念是不对Syscall状态的协程发送信号。
557- //但由于NOTIFY_NODE移除和monitor线程遍历之间存在竞态条件,
558- //SIGURG可能在协程刚进入Syscall状态时到达。
559- //如果此时抢占,协程会被放入syscall_map但无人唤醒(因为没有io_uring/epoll注册),
560- //导致死锁。
561- // Skip preemption for coroutines in Syscall state.
562- // MonitorListener's design is to NOT send signals to Syscall-state
563- // coroutines. However, a race between NOTIFY_NODE removal and the
564- // monitor's queue iteration can cause SIGURG to arrive just after
565- // the coroutine entered Syscall state. If preempted here, the
566- // coroutine lands in the syscall map with no io_uring/epoll/timer
567- // registration to wake it, causing a deadlock.
568- if let Some ( co) = SchedulableCoroutine :: current ( ) {
569- if matches ! ( co. state( ) , CoroutineState :: Syscall ( ( ) , _, _) ) {
570- return ;
571- }
572- }
573- if let Some ( suspender) = SchedulableSuspender :: current ( ) {
574- suspender. suspend ( ) ;
575- }
576- } else {
577- // First attempt: set the flag and return without suspending.
578- // preempt_asm will restore all registers and return to the original
579- // code. This gives the thread time to exit any critical section.
580- // If the coroutine reaches a hooked syscall, the Nio/Iocp layer
581- // will yield cooperatively via Suspender::suspend_with.
582- flag. set ( true ) ;
533+ //MonitorListener的设计理念是只对Running状态的协程发送信号。
534+ //但由于NOTIFY_NODE移除和monitor线程遍历之间存在竞态条件,
535+ //SIGURG可能在协程刚进入Syscall状态时到达。
536+ //如果此时抢占,协程会被放入syscall_map但无人唤醒(因为没有io_uring/epoll注册),导致死锁。
537+ // Skip preemption for coroutines in Syscall state.
538+ // MonitorListener's design is to NOT send signals to Syscall-state
539+ // coroutines. However, a race between NOTIFY_NODE removal and the
540+ // monitor's queue iteration can cause SIGURG to arrive just after
541+ // the coroutine entered Syscall state. If preempted here, the
542+ // coroutine lands in the syscall map with no io_uring/epoll/timer
543+ // registration to wake it, causing a deadlock.
544+ if let Some ( co) = SchedulableCoroutine :: current ( ) {
545+ if !matches ! ( co. state( ) , CoroutineState :: Running ) {
546+ return ;
583547 }
584- } ) ;
548+ }
549+ if let Some ( suspender) = SchedulableSuspender :: current ( ) {
550+ suspender. suspend ( ) ;
551+ }
585552}
586553
587554#[ repr( C ) ]
@@ -701,14 +668,6 @@ mod tests {
701668 assert_ne ! ( thread_id, 0 , "Thread should have reported its ID" ) ;
702669
703670 // Directly call preempt_thread to preempt the running coroutine.
704- // Two-level preemption: the first call sets a cooperative flag (the
705- // coroutine continues running), the second call forces suspension.
706- assert ! (
707- super :: Monitor :: preempt_thread( thread_id) ,
708- "preempt_thread should succeed (set cooperative flag)"
709- ) ;
710- // Allow the first preempt_asm to complete before the second call
711- std:: thread:: sleep ( Duration :: from_millis ( 1 ) ) ;
712671 assert ! (
713672 super :: Monitor :: preempt_thread( thread_id) ,
714673 "preempt_thread should succeed (force suspend)"
0 commit comments