Skip to content

Commit 96f44ac

Browse files
committed
Apply review based on cpython
1 parent 8a9096a commit 96f44ac

File tree

2 files changed

+64
-110
lines changed

2 files changed

+64
-110
lines changed

crates/stdlib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ features = [
150150
"Win32_NetworkManagement_Ndis",
151151
"Win32_Security_Cryptography",
152152
"Win32_Storage_FileSystem",
153+
"Win32_System_Diagnostics_Debug",
153154
"Win32_System_Environment",
154155
"Win32_System_IO",
155156
"Win32_System_Threading"

crates/stdlib/src/faulthandler.rs

Lines changed: 63 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -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, "\nFatal 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, "\nFatal 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

Comments
 (0)