mod init {
use std::{
io,
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
};
static IS_INITIALIZED: AtomicBool = AtomicBool::new(false);
#[derive(Default)]
pub struct Deregister(Vec<(i32, signal_hook::SigId)>);
pub struct AutoDeregister(Deregister);
impl Deregister {
pub fn deregister(self) -> std::io::Result<()> {
if self.0.is_empty() {
return Ok(());
}
static REINSTATE_DEFAULT_BEHAVIOUR: AtomicBool = AtomicBool::new(true);
for (_, hook_id) in &self.0 {
signal_hook::low_level::unregister(*hook_id);
}
IS_INITIALIZED.store(false, Ordering::SeqCst);
if REINSTATE_DEFAULT_BEHAVIOUR
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| Some(false))
.expect("always returns value")
{
for (sig, _) in self.0 {
#[allow(unsafe_code)]
unsafe {
signal_hook::low_level::register(sig, move || {
signal_hook::low_level::emulate_default_handler(sig).ok();
})?;
}
}
}
Ok(())
}
pub fn auto_deregister(self) -> AutoDeregister {
AutoDeregister(self)
}
}
impl Drop for AutoDeregister {
fn drop(&mut self) {
std::mem::take(&mut self.0).deregister().ok();
}
}
pub fn init_handler(interrupt: impl Fn() + Send + Sync + Clone + 'static) -> io::Result<Deregister> {
if IS_INITIALIZED
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| Some(true))
.expect("always returns value")
{
return Err(io::Error::new(io::ErrorKind::Other, "Already initialized"));
}
let mut hooks = Vec::with_capacity(signal_hook::consts::TERM_SIGNALS.len());
for sig in signal_hook::consts::TERM_SIGNALS {
let interrupt = interrupt.clone();
#[allow(unsafe_code)]
unsafe {
let hook_id = signal_hook::low_level::register(*sig, move || {
static INTERRUPT_COUNT: AtomicUsize = AtomicUsize::new(0);
if !super::is_triggered() {
INTERRUPT_COUNT.store(0, Ordering::SeqCst);
}
let msg_idx = INTERRUPT_COUNT.fetch_add(1, Ordering::SeqCst);
if msg_idx == 1 {
git_tempfile::handler::cleanup_tempfiles();
signal_hook::low_level::emulate_default_handler(*sig).ok();
}
interrupt();
super::trigger();
})?;
hooks.push((*sig, hook_id));
}
}
git_tempfile::setup(git_tempfile::SignalHandlerMode::None);
Ok(Deregister(hooks))
}
}
use std::{
io,
sync::atomic::{AtomicBool, Ordering},
};
pub use init::init_handler;
pub struct Iter<I, EFN> {
inner: git_features::interrupt::IterWithErr<'static, I, EFN>,
}
impl<I, EFN, E> Iter<I, EFN>
where
I: Iterator,
EFN: FnOnce() -> E,
{
pub fn new(inner: I, make_err: EFN) -> Self {
Iter {
inner: git_features::interrupt::IterWithErr::new(inner, make_err, &IS_INTERRUPTED),
}
}
pub fn into_inner(self) -> I {
self.inner.inner
}
pub fn inner(&self) -> &I {
&self.inner.inner
}
}
impl<I, EFN, E> Iterator for Iter<I, EFN>
where
I: Iterator,
EFN: FnOnce() -> E,
{
type Item = Result<I::Item, E>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
pub struct Read<R> {
inner: git_features::interrupt::Read<'static, R>,
}
impl<R> Read<R>
where
R: io::Read,
{
pub fn new(read: R) -> Self {
Read {
inner: git_features::interrupt::Read {
inner: read,
should_interrupt: &IS_INTERRUPTED,
},
}
}
pub fn into_inner(self) -> R {
self.inner.inner
}
}
impl<R> io::Read for Read<R>
where
R: io::Read,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
}
impl<R> io::BufRead for Read<R>
where
R: io::BufRead,
{
fn fill_buf(&mut self) -> io::Result<&[u8]> {
self.inner.fill_buf()
}
fn consume(&mut self, amt: usize) {
self.inner.consume(amt)
}
}
pub static IS_INTERRUPTED: AtomicBool = AtomicBool::new(false);
pub fn is_triggered() -> bool {
IS_INTERRUPTED.load(Ordering::Relaxed)
}
pub fn trigger() {
IS_INTERRUPTED.store(true, Ordering::SeqCst);
}
pub fn reset() {
IS_INTERRUPTED.store(false, Ordering::SeqCst);
}