调用println!同时召唤恐慌

时间:2017-07-01 21:56:53

标签: rust

我正在尝试处理一些* nix信号并具有以下代码:

// nix = "0.8.0"
extern crate nix;

use nix::sys::signal::{SaFlags, SigAction, sigaction, SIGINT, SigSet};
use nix::sys::signal::SigHandler::Handler;

extern fn interrupted(_:i32) {
    println!("Interrupted");
}

fn main() {
    let sigact = SigAction::new(Handler(interrupted), SaFlags::empty(), SigSet::thread_get_mask().unwrap());

    if let Err(_) = unsafe { sigaction(SIGINT, &sigact) } {
        println!("Failed to register handler");
        std::process::exit(1);
    }

    loop {
        println!("Hello");
    }
}

我认为可以同时调用println!(),但是当我中断

时,这段代码总是会引起恐慌
thread 'main' panicked at 'already borrowed: BorrowMutError', /checkout/src/libcore/result.rs:859
stack backtrace:
   [...]
  10: <std::io::stdio::StdoutLock<'a> as std::io::Write>::write
             at /checkout/src/libcore/result.rs:761
             at /checkout/src/libcore/cell.rs:670
             at /checkout/src/libstd/io/stdio.rs:466
  11: std::io::Write::write_all
             at /checkout/src/libstd/io/mod.rs:957
  12: <std::io::Write::write_fmt::Adaptor<'a, T> as core::fmt::Write>::write_str
             at /checkout/src/libstd/io/mod.rs:1017
  13: core::fmt::write
             at /checkout/src/libcore/fmt/mod.rs:969
  14: <std::io::stdio::Stdout as std::io::Write>::write_fmt
             at /checkout/src/libstd/io/mod.rs:1028
             at /checkout/src/libstd/io/stdio.rs:460
  15: std::io::stdio::_print
             at /checkout/src/libstd/io/stdio.rs:680
  16: signals::interrupted
             at ./Rust/signals/src/main.rs:8
   [...]

println!()的文档没有特别提及并发执行的任何恐慌,只是当它无法写入stdout时会感到恐慌。我对这种恐慌感到有些困惑,因为从循环中的多个线程调用println!()可以正常工作。

问题是println!()是否是线程安全的,但在同一个线程上不安全,即println!()main的第一个实例时有一些线程本地静态依赖项被违反被中断,interrupted中的第二个实例在同一个线程上被调用,或者我怎么能想象这个呢?

1 个答案:

答案 0 :(得分:4)

  

问题是println!()是否是线程安全的,但在同一个线程上不安全,即println!()main的第一个实例时有一些线程本地静态依赖项被违反被中断,interrupted中的第二个实例在同一个线程[...]上调用?

是的,这是关于正确的。

println!()代表print!()代表std::io::_print,调用RefCell,通过传递print_to作为参数来调用&LOCAL_STDOUTLOCAL_STDOUT的定义如下:

/// Stdout used by print! and println! macros
thread_local! {
    static LOCAL_STDOUT: RefCell<Option<Box<Write + Send>>> = {
        RefCell::new(None)
    }
}

您看到的恐慌来自LOCAL_STDOUT使用的Array.prototype.map()。信号处理程序在接收信号的线程上运行。因此,当发出信号时,信号处理程序可以在RefCell可变地借用的同时运行。由于RefCell一次只允许一次可变借用,因此尝试在信号处理程序中使用println!()可能会引起恐慌。