@@ -502,12 +502,39 @@ fn switch_to_thread(
502502 }
503503
504504 // Now deliver the signal (modifies interrupt_frame and saved_regs)
505- if crate :: signal:: delivery:: deliver_pending_signals (
505+ let signal_result = crate :: signal:: delivery:: deliver_pending_signals (
506506 process,
507507 interrupt_frame,
508508 saved_regs,
509- ) {
510- log:: info!( "Signal delivered to thread {}" , thread_id) ;
509+ ) ;
510+
511+ // Handle signal result
512+ match signal_result {
513+ crate :: signal:: delivery:: SignalDeliveryResult :: Terminated ( n) => {
514+ log:: info!( "Signal terminated process, thread {}" , thread_id) ;
515+ // Process was terminated - notify parent after releasing locks
516+ // We need to return from this function and let the locks drop naturally
517+ // but first save the notification data
518+ // We have the notification info but can't call notify while holding locks.
519+ // The notification will be handled by the timer interrupt when it
520+ // eventually switches to the parent process, because:
521+ // 1. The child is now marked as terminated
522+ // 2. When the parent's waitpid resumes, it will find the terminated child
523+ // 3. The scheduler will see the parent is unblocked
524+ // However, this path is rare (signal terminating a process whose parent
525+ // is blocked in waitpid *at the exact same time*).
526+ // The parent notification happens in the other code paths.
527+ log:: debug!(
528+ "Signal termination in blocked_in_syscall path: parent {} will be notified when resumed" ,
529+ n. parent_pid. as_u64( )
530+ ) ;
531+ // Just return - RAII will release the locks
532+ return ;
533+ }
534+ crate :: signal:: delivery:: SignalDeliveryResult :: Delivered => {
535+ log:: info!( "Signal delivered to thread {}" , thread_id) ;
536+ }
537+ crate :: signal:: delivery:: SignalDeliveryResult :: NoAction => { }
511538 }
512539 } else {
513540 // NO PENDING SIGNALS: Resume at kernel HLT loop
@@ -764,7 +791,7 @@ fn restore_userspace_thread_context(
764791 let guard_option = process_manager_guard. or_else ( || crate :: process:: try_manager ( ) ) ;
765792
766793 // Track if signal termination happened (for parent notification after borrow ends)
767- let mut signal_termination_info: Option < ( crate :: process :: ProcessId , crate :: process :: ProcessId ) > = None ;
794+ let mut signal_termination_info: Option < crate :: signal :: delivery :: ParentNotification > = None ;
768795
769796 if let Some ( mut manager_guard) = guard_option {
770797 if let Some ( ref mut manager) = * manager_guard {
@@ -854,53 +881,44 @@ fn restore_userspace_thread_context(
854881 ) ;
855882 }
856883
857- // Save parent info BEFORE delivering signal, in case the signal
858- // terminates the process and we need to notify parent
859- let child_pid = pid;
860- let parent_pid = process. parent ;
861-
862- if crate :: signal:: delivery:: deliver_pending_signals (
884+ // Deliver pending signals
885+ let signal_result = crate :: signal:: delivery:: deliver_pending_signals (
863886 process,
864887 interrupt_frame,
865888 saved_regs,
866- ) {
867- // Signal was delivered and frame was modified
868- // If process was terminated, save info for parent notification
869- if process. is_terminated ( ) {
889+ ) ;
890+
891+ match signal_result {
892+ crate :: signal:: delivery:: SignalDeliveryResult :: Terminated ( notification) => {
893+ // Signal terminated the process
870894 crate :: task:: scheduler:: set_need_resched ( ) ;
871- // Save for later parent notification
872- if let Some ( ppid) = parent_pid {
873- signal_termination_info = Some ( ( child_pid, ppid) ) ;
895+ // Save notification for later (after manager lock is released)
896+ signal_termination_info = Some ( notification) ;
897+ setup_idle_return ( interrupt_frame) ;
898+ crate :: task:: scheduler:: switch_to_idle ( ) ;
899+ // Don't return here - fall through to handle notification
900+ }
901+ crate :: signal:: delivery:: SignalDeliveryResult :: Delivered => {
902+ // Signal was delivered and frame was modified
903+ if process. is_terminated ( ) {
904+ // Process was terminated (somehow?)
905+ crate :: task:: scheduler:: set_need_resched ( ) ;
906+ setup_idle_return ( interrupt_frame) ;
907+ crate :: task:: scheduler:: switch_to_idle ( ) ;
874908 }
875909 }
910+ crate :: signal:: delivery:: SignalDeliveryResult :: NoAction => { }
876911 }
877912 }
878913 }
879914 }
880915 }
881916
882917 // Now process borrow has ended - notify parent if signal terminated a child
883- if let Some ( ( child_pid, parent_pid) ) = signal_termination_info {
884- if let Some ( parent_process) = manager. get_process_mut ( parent_pid) {
885- use crate :: signal:: constants:: SIGCHLD ;
886- parent_process. signals . set_pending ( SIGCHLD ) ;
887-
888- // Get parent's main thread ID to wake it if blocked on waitpid
889- let parent_thread_id = parent_process. main_thread . as_ref ( ) . map ( |t| t. id ( ) ) ;
890-
891- log:: info!(
892- "Signal termination: sent SIGCHLD to parent {} for child {} exit" ,
893- parent_pid. as_u64( ) ,
894- child_pid. as_u64( )
895- ) ;
896-
897- // Wake up the parent thread if it's blocked on waitpid
898- if let Some ( parent_tid) = parent_thread_id {
899- scheduler:: with_scheduler ( |sched| {
900- sched. unblock_for_child_exit ( parent_tid) ;
901- } ) ;
902- }
903- }
918+ // Drop manager guard first to avoid deadlock when notifying parent
919+ drop ( manager_guard) ;
920+ if let Some ( notification) = signal_termination_info {
921+ crate :: signal:: delivery:: notify_parent_of_termination_deferred ( & notification) ;
904922 }
905923 }
906924 } else {
@@ -1058,11 +1076,11 @@ fn check_and_deliver_signals_for_current_thread(
10581076 } ;
10591077
10601078 // Track if signal termination happened (for parent notification after borrow ends)
1061- let mut signal_termination_info: Option < ( crate :: process :: ProcessId , crate :: process :: ProcessId ) > = None ;
1079+ let mut signal_termination_info: Option < crate :: signal :: delivery :: ParentNotification > = None ;
10621080
10631081 if let Some ( ref mut manager) = * manager_guard {
10641082 // Find the process for this thread
1065- if let Some ( ( pid , process) ) = manager. find_process_by_thread_mut ( current_thread_id) {
1083+ if let Some ( ( _pid , process) ) = manager. find_process_by_thread_mut ( current_thread_id) {
10661084 // Note: Debug logging removed from hot path - use GDB if debugging is needed
10671085 if crate :: signal:: delivery:: has_deliverable_signals ( process) {
10681086 // Switch to process's page table for signal delivery
@@ -1080,52 +1098,41 @@ fn check_and_deliver_signals_for_current_thread(
10801098 }
10811099 }
10821100
1083- // Save parent info BEFORE delivering signal, in case the signal
1084- // terminates the process and we need to notify parent
1085- let child_pid = pid;
1086- let parent_pid = process. parent ;
1087-
10881101 // Deliver signals
1089- if crate :: signal:: delivery:: deliver_pending_signals (
1102+ let signal_result = crate :: signal:: delivery:: deliver_pending_signals (
10901103 process,
10911104 interrupt_frame,
10921105 saved_regs,
1093- ) {
1094- // Signal was delivered (may have terminated process)
1095- if process. is_terminated ( ) {
1106+ ) ;
1107+
1108+ match signal_result {
1109+ crate :: signal:: delivery:: SignalDeliveryResult :: Terminated ( notification) => {
1110+ // Signal terminated the process
10961111 crate :: task:: scheduler:: set_need_resched ( ) ;
1097- // Save for later parent notification (after borrow ends)
1098- if let Some ( ppid) = parent_pid {
1099- signal_termination_info = Some ( ( child_pid, ppid) ) ;
1112+ signal_termination_info = Some ( notification) ;
1113+ setup_idle_return ( interrupt_frame) ;
1114+ crate :: task:: scheduler:: switch_to_idle ( ) ;
1115+ // Don't return here - fall through to handle notification
1116+ }
1117+ crate :: signal:: delivery:: SignalDeliveryResult :: Delivered => {
1118+ if process. is_terminated ( ) {
1119+ crate :: task:: scheduler:: set_need_resched ( ) ;
1120+ setup_idle_return ( interrupt_frame) ;
1121+ crate :: task:: scheduler:: switch_to_idle ( ) ;
11001122 }
11011123 }
1124+ crate :: signal:: delivery:: SignalDeliveryResult :: NoAction => { }
11021125 }
11031126 }
11041127 }
11051128 // process borrow has ended here
11061129
1107- // Notify parent if signal terminated a child
1108- if let Some ( ( child_pid, parent_pid) ) = signal_termination_info {
1109- if let Some ( parent_process) = manager. get_process_mut ( parent_pid) {
1110- use crate :: signal:: constants:: SIGCHLD ;
1111- parent_process. signals . set_pending ( SIGCHLD ) ;
1112-
1113- // Get parent's main thread ID to wake it if blocked on waitpid
1114- let parent_thread_id = parent_process. main_thread . as_ref ( ) . map ( |t| t. id ( ) ) ;
1130+ // Drop manager guard first to avoid deadlock when notifying parent
1131+ drop ( manager_guard) ;
11151132
1116- log:: info!(
1117- "Signal termination (no-switch path): sent SIGCHLD to parent {} for child {} exit" ,
1118- parent_pid. as_u64( ) ,
1119- child_pid. as_u64( )
1120- ) ;
1121-
1122- // Wake up the parent thread if it's blocked on waitpid
1123- if let Some ( parent_tid) = parent_thread_id {
1124- scheduler:: with_scheduler ( |sched| {
1125- sched. unblock_for_child_exit ( parent_tid) ;
1126- } ) ;
1127- }
1128- }
1133+ // Notify parent if signal terminated a child
1134+ if let Some ( notification) = signal_termination_info {
1135+ crate :: signal:: delivery:: notify_parent_of_termination_deferred ( & notification) ;
11291136 }
11301137 }
11311138}
0 commit comments