@@ -111,11 +111,15 @@ mod decl {
111111
112112 fn collect_frame_info ( frame : & crate :: vm:: PyRef < Frame > ) -> String {
113113 let func_name = truncate_name ( frame. code . obj_name . as_str ( ) ) ;
114+ // If lasti is 0, execution hasn't started yet - use first line number or 1
115+ let line = if frame. lasti ( ) == 0 {
116+ frame. code . first_line_number . map ( |n| n. get ( ) ) . unwrap_or ( 1 )
117+ } else {
118+ frame. current_location ( ) . line . get ( )
119+ } ;
114120 format ! (
115121 " File \" {}\" , line {} in {}" ,
116- frame. code. source_path,
117- frame. current_location( ) . line,
118- func_name
122+ frame. code. source_path, line, func_name
119123 )
120124 }
121125
@@ -171,37 +175,34 @@ mod decl {
171175 }
172176
173177 /// Unix signal handler for fatal errors
178+ // faulthandler_fatal_error() in Modules/faulthandler.c
174179 #[ cfg( unix) ]
175180 extern "C" fn faulthandler_fatal_error ( signum : libc:: c_int ) {
176- // Reentrant protection
177- static REENTRANT : AtomicBool = AtomicBool :: new ( false ) ;
178- if REENTRANT . swap ( true , Ordering :: SeqCst ) {
181+ if !ENABLED . load ( Ordering :: Relaxed ) {
179182 return ;
180183 }
181184
182185 let fd = FATAL_ERROR_FD . load ( Ordering :: Relaxed ) ;
183186
184- // Find and print signal name
185- let signal_name = FATAL_SIGNALS
186- . iter ( )
187- . find ( |( sig, _) | * sig == signum)
188- . map ( |( _, name) | * name)
189- . unwrap_or ( "Unknown signal" ) ;
187+ // Find handler and restore previous handler BEFORE printing
188+ let signal_name =
189+ if let Some ( idx) = FATAL_SIGNALS . iter ( ) . position ( |( sig, _) | * sig == signum) {
190+ // Restore previous handler first
191+ unsafe {
192+ libc:: sigaction ( signum, & PREVIOUS_HANDLERS [ idx] , std:: ptr:: null_mut ( ) ) ;
193+ }
194+ FATAL_SIGNALS [ idx] . 1
195+ } else {
196+ "Unknown signal"
197+ } ;
190198
191- write_str_noraise ( fd, "\n Fatal Python error: " ) ;
199+ // Print error message
200+ write_str_noraise ( fd, "Fatal Python error: " ) ;
192201 write_str_noraise ( fd, signal_name) ;
193202 write_str_noraise ( fd, "\n \n " ) ;
194203
195- // Restore previous handler
196- if let Some ( idx) = FATAL_SIGNALS . iter ( ) . position ( |( sig, _) | * sig == signum) {
197- unsafe {
198- libc:: sigaction ( signum, & PREVIOUS_HANDLERS [ idx] , std:: ptr:: null_mut ( ) ) ;
199- }
200- }
201-
202- REENTRANT . store ( false , Ordering :: SeqCst ) ;
203-
204204 // Re-raise signal to trigger default behavior (core dump, etc.)
205+ // Called immediately thanks to SA_NODEFER flag
205206 unsafe {
206207 libc:: raise ( signum) ;
207208 }
@@ -211,7 +212,7 @@ mod decl {
211212 fn install_fatal_handlers ( _vm : & VirtualMachine ) {
212213 for ( idx, ( signum, _) ) in FATAL_SIGNALS . iter ( ) . enumerate ( ) {
213214 let mut action: libc:: sigaction = unsafe { std:: mem:: zeroed ( ) } ;
214- action. sa_sigaction = faulthandler_fatal_error as usize ;
215+ action. sa_sigaction = faulthandler_fatal_error as libc :: sighandler_t ;
215216 action. sa_flags = libc:: SA_NODEFER ;
216217
217218 unsafe {
@@ -220,41 +221,43 @@ mod decl {
220221 }
221222 }
222223
223- /// Windows signal handler for fatal errors (simpler than VEH)
224+ /// Windows signal handler for fatal errors
225+ // faulthandler_fatal_error() in Modules/faulthandler.c
224226 #[ cfg( windows) ]
225227 extern "C" fn faulthandler_fatal_error_windows ( signum : libc:: c_int ) {
226- // Reentrant protection
227- static REENTRANT : AtomicBool = AtomicBool :: new ( false ) ;
228- if REENTRANT . swap ( true , Ordering :: SeqCst ) {
228+ if !ENABLED . load ( Ordering :: Relaxed ) {
229229 return ;
230230 }
231231
232232 let fd = FATAL_ERROR_FD . load ( Ordering :: Relaxed ) ;
233233
234- // Find and print signal name
235- let signal_name = FATAL_SIGNALS
236- . iter ( )
237- . find ( |( sig, _) | * sig == signum)
238- . map ( |( _, name) | * name)
239- . unwrap_or ( "Unknown signal" ) ;
234+ // Find handler and restore previous handler BEFORE printing
235+ let signal_name =
236+ if let Some ( idx) = FATAL_SIGNALS . iter ( ) . position ( |( sig, _) | * sig == signum) {
237+ // Restore previous handler first
238+ unsafe {
239+ libc:: signal ( signum, PREVIOUS_HANDLERS_WIN [ idx] ) ;
240+ }
241+ FATAL_SIGNALS [ idx] . 1
242+ } else {
243+ "Unknown signal"
244+ } ;
240245
241- write_str_noraise ( fd, "\n Fatal Python error: " ) ;
246+ // Print error message
247+ write_str_noraise ( fd, "Fatal Python error: " ) ;
242248 write_str_noraise ( fd, signal_name) ;
243249 write_str_noraise ( fd, "\n \n " ) ;
244250
245- REENTRANT . store ( false , Ordering :: SeqCst ) ;
246-
247- // For SIGSEGV on Windows, need to loop raise
251+ // On Windows, don't explicitly call the previous handler for SIGSEGV
252+ // The execution continues and the same instruction raises the same fault,
253+ // calling the now-restored previous handler
248254 if signum == libc:: SIGSEGV {
249- loop {
250- unsafe {
251- libc:: raise ( signum) ;
252- }
253- }
254- } else {
255- unsafe {
256- libc:: raise ( signum) ;
257- }
255+ return ;
256+ }
257+
258+ // For other signals, re-raise to call the previous handler
259+ unsafe {
260+ libc:: raise ( signum) ;
258261 }
259262 }
260263
@@ -367,36 +370,25 @@ mod decl {
367370 let ( lock, cvar) = & * state;
368371
369372 loop {
370- let ( timeout, repeat, exit, fd, header, cancelled) = {
371- let guard = lock. lock ( ) . unwrap ( ) ;
372- if guard. cancel {
373- return ;
374- }
375- (
376- Duration :: from_micros ( guard. timeout_us ) ,
377- guard. repeat ,
378- guard. exit ,
379- guard. fd ,
380- guard. header . clone ( ) ,
381- guard. cancel ,
382- )
383- } ;
384-
385- if cancelled {
373+ // Hold lock across wait_timeout to avoid race condition
374+ let mut guard = lock. lock ( ) . unwrap ( ) ;
375+ if guard. cancel {
386376 return ;
387377 }
378+ let timeout = Duration :: from_micros ( guard. timeout_us ) ;
379+ let result = cvar. wait_timeout ( guard, timeout) . unwrap ( ) ;
380+ guard = result. 0 ;
388381
389- // Wait for timeout or cancellation
390- let result = {
391- let guard = lock. lock ( ) . unwrap ( ) ;
392- cvar. wait_timeout ( guard, timeout) . unwrap ( )
393- } ;
394-
395- // Check if cancelled
396- if result. 0 . cancel {
382+ // Check if cancelled after wait
383+ if guard. cancel {
397384 return ;
398385 }
399386
387+ // Extract values before releasing lock for I/O
388+ let ( repeat, exit, fd, header) =
389+ ( guard. repeat , guard. exit , guard. fd , guard. header . clone ( ) ) ;
390+ drop ( guard) ; // Release lock before I/O
391+
400392 // Timeout occurred, dump traceback
401393 #[ cfg( not( target_arch = "wasm32" ) ) ]
402394 {
@@ -652,45 +644,6 @@ mod decl {
652644 Ok ( ( ) )
653645 }
654646
655- #[ cfg( unix) ]
656- fn get_fd_from_file ( file : OptionalArg < PyObjectRef > , vm : & VirtualMachine ) -> PyResult < i32 > {
657- match file {
658- OptionalArg :: Present ( f) => {
659- // Check if it's an integer (file descriptor)
660- if let Ok ( fd) = f. try_to_value :: < i32 > ( vm) {
661- if fd < 0 {
662- return Err (
663- vm. new_value_error ( "file is not a valid file descriptor" . to_owned ( ) )
664- ) ;
665- }
666- return Ok ( fd) ;
667- }
668- // Try to get fileno() from file object
669- let fileno = vm. call_method ( & f, "fileno" , ( ) ) ?;
670- let fd: i32 = fileno. try_to_value ( vm) ?;
671- if fd < 0 {
672- return Err (
673- vm. new_value_error ( "file is not a valid file descriptor" . to_owned ( ) )
674- ) ;
675- }
676- // Try to flush the file
677- let _ = vm. call_method ( & f, "flush" , ( ) ) ;
678- Ok ( fd)
679- }
680- OptionalArg :: Missing => {
681- // Get sys.stderr
682- let stderr = vm. sys_module . get_attr ( "stderr" , vm) ?;
683- if vm. is_none ( & stderr) {
684- return Err ( vm. new_runtime_error ( "sys.stderr is None" . to_owned ( ) ) ) ;
685- }
686- let fileno = vm. call_method ( & stderr, "fileno" , ( ) ) ?;
687- let fd: i32 = fileno. try_to_value ( vm) ?;
688- let _ = vm. call_method ( & stderr, "flush" , ( ) ) ;
689- Ok ( fd)
690- }
691- }
692- }
693-
694647 #[ cfg( unix) ]
695648 #[ derive( FromArgs ) ]
696649 #[ allow( unused) ]
@@ -710,7 +663,7 @@ mod decl {
710663 fn register ( args : RegisterArgs , vm : & VirtualMachine ) -> PyResult < ( ) > {
711664 check_signum ( args. signum , vm) ?;
712665
713- let fd = get_fd_from_file ( args. file , vm) ?;
666+ let fd = get_fd_from_file_opt ( args. file , vm) ?;
714667
715668 let signum = args. signum as usize ;
716669
0 commit comments