Skip to content

Commit c2a87c9

Browse files
committed
less syscall
1 parent 570c679 commit c2a87c9

File tree

1 file changed

+25
-66
lines changed

1 file changed

+25
-66
lines changed

core/src/monitor.rs

Lines changed: 25 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
548532
extern "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

Comments
 (0)