#interrupt #signal #posix #signals #builder #api-bindings #xvrqt

addy

POSIX interrupt handling that is both safe and ergonomic

3 unstable releases

0.1.1 Mar 26, 2020
0.1.0 Mar 26, 2020
0.0.1 Mar 25, 2020

#483 in Unix APIs


Used in krecik

Custom license

49KB
421 lines

Addy

A library for ergonomically handling kernel interrupts.

Quick Start

use addy::SIGWINCH;
use std::io::{Read, stdin};
fn main() -> Result<(), addy::Error> {
	/* SIGWINCH is a POSIX interrupt signal for window resized */
	addy::mediate(SIGWINCH)
			.register("print", |_signal| { println!("Screen Resized!"); })?
			.enable()?;

	/* Block so the program doesn't exit immediately 
	 * Try resizing your terminal window :)
	*/
	let mut buffer = [0; 1];
   	loop {
       stdin().read(&mut buffer);
  	}
	Ok(())
}

Functions

Mediate

This gives you a SignalHandle representing the interrupt handler you want to interact with. SignalHandles are threadsafe! Call/create/move them anywhere from anywhere!

use addy::SIGWINCH;
use std::io::{Read, stdin};

fn main() -> Result<(), addy::Error> {
	addy::mediate(SIGWINCH)
			.register("resized", |_signal| { println!("Screen Resized!"); })?
			.enable()?;

	/* Block so the program doesn't exit immediately 
	 * Try resizing your terminal window :)
	*/
	let mut buffer = [0; 1];
	loop {
    	stdin().read(&mut buffer);
	}

	Ok(())
}

Register

Registers a callback with the interrupt handler for the associated Signal. If you call register with the same name it will replace the previous callback.

use addy::{Signal, SIGWINCH};
fn my_func(signal: Signal) {
	/* Does a thing */
}
fn main() -> Result<(), addy::Error> {
	addy::mediate(SIGWINCH)
			.register("print", |_signal| { println!("Screen Resized!"); })?
			.register("my_func", my_func)?
			.enable()?;
	Ok(())
}

Enable

Begins capturing the interrupt and calling any associated callbacks. Most often used after a calls .register()

Alias of .resume()

 use addy::SIGINT;

 fn main() -> Result<(), addy::Error> {
 	addy::mediate(SIGINT)
				.register("print", |_signal| { println!("Interrupted!"); })?
				.enable()?;
		Ok(())
 }

Remove

Removes a named callback from the associated Signal. If no callback with that name exists, it does nothing.

use addy::{Signal, SIGWINCH};
fn my_func(signal: Signal) {
	/* Does a thing */
}
fn main() -> Result<(), addy::Error> {
	addy::mediate(SIGWINCH)
			.register("print", |_signal| { println!("Screen Resized!"); })?
			.register("my_func", my_func)?
			.enable()?;
	//-- Later --//
	// Stop calling "print" when the process receives a SIGWINCH signal
	addy::mediate(SIGWINCH).remove("print")?;
	Ok(())
}

Clear

Removes a all callbacks from the associated Signal. Functionally similar to calling .ignore() except you don't need to call .enable() if you add new callbacks later.

use addy::{Signal, SIGWINCH};

fn my_func(signal: Signal) {
	/* Does a thing */
}

fn main() -> Result<(), addy::Error> {
	addy::mediate(SIGWINCH)
			.register("print", |_signal| { println!("Screen Resized!"); })?
			.register("my_func", my_func)?
			.enable()?;

	//-- Later --//

	// Capture the signal, but stop calling anything
	addy::mediate(SIGWINCH)
			.clear()?
			.register("solo_callback", |_signal| { println!("ALONE!"); })?;

	Ok(())
}

Release

Removes a all callbacks from the associated Signal and resets the interrupt handler to the default behavior. Funcationally the same as calling .clear() and .default() You will need to call .enable() again after re-registering callbacks.

use addy::SIGWINCH;

fn main() -> Result<(), addy::Error> {
	addy::mediate(SIGWINCH)
			.register("print", |_signal| { println!("Screen Resized!"); })?
			.enable()?;

	//-- Later --//

	// Stop capturing the signal
	addy::mediate(SIGWINCH).release()?;

	//-- Later Still --//

	// Start catpuring again
	addy::mediate(SIGWINCH)
			.register("new", |_signal| { println!("New callback!"); })?
			.enable()?;

	Ok(())
}

Ignore

Tells the process to ignore this interrupt. Keeps all your callbacks. Calling .resume() will re-enable them.

use addy::SIGWINCH;

fn main() -> Result<(), addy::Error> {
	addy::mediate(SIGWINCH)
			.register("print", |_signal| { println!("Screen Resized!"); })?
			.enable()?;

	//-- Later --//

	// Ignore the signal
	addy::mediate(SIGWINCH).ignore()?;

	//-- Later Still --//

	// Start catpuring again
	addy::mediate(SIGWINCH).resume()?;

	Ok(())
}

Default

Restore the interrupt handler to the system default. Not all interrupts have a default, and some interrupts default is to be ignored. Keeps all your callbacks. Calling .resume() will re-enable them.

use addy::SIGINT;

fn main() -> Result<(), addy::Error> {
	addy::mediate(SIGINT)
			.register("print", |_signal| { println!("Interrupted!"); })?
			.enable()?;

	//-- Later --//

	// Set the signal to its default
	addy::mediate(SIGINT).default()?;

	//-- Later Still --//

	// Start catpuring again
	addy::mediate(SIGINT).resume()?;

	Ok(())
}

Resume

Resumes capturing the interrupt and calling any associated callbacks. Most often used after a call to .ignore() and .default().

Alias of .enable()

use addy::SIGINT;

fn main() -> Result<(), addy::Error> {
	addy::mediate(SIGINT)
			.register("print", |_signal| { println!("Interrupted!"); })?
			.enable()?;

	//-- Later --//

	// Set the signal to its default
	addy::mediate(SIGINT).default()?;

	//-- Later Still --//

	// Start catpuring and printing "Interrupted!" again
	addy::mediate(SIGINT).resume()?;

	Ok(())
}

Things To Know

I love you and I wish the best for you. No matter what you choose to do, I hope you decide it is worth you time to do it well.

Addy is Thread Safe!

You can call it from anywhere, at anytime! You can store a SignalHandle (returned from addy::mediate(signal)) in a variable and pass it around.

use addy::{SIGWINCH, SIGINT};
use std::io::{Read, stdin};
static QUOTE: &'static str = "Look at you, hacker: a pathetic creature of meat \
							  and bone, panting and sweating as you run through \
							  my corridors. How can you challenge a perfect, \
							  immortal machine?";

fn main() -> Result<(), addy::Error> {
	/* When the window resizes */
    addy::mediate(SIGWINCH)
    		.register("hello", |_signal| { println!("Hello, World!"); })?
    		.register("girls", |_signal| { println!("Hello, Girls!"); })?
    		.enable()?;

    /* SIGINT is sent when the user presses Ctrl + C. The default behavior is 
     * to interrupt the program's execution.
    */
    let mut ctrl_c = addy::mediate(SIGINT);
    ctrl_c.register("no_interruptions", |_signal| { println!("{}", QUOTE); })?.enable()?;

    /* Let the user use Ctrl + C to kill the program after 10 seconds */
    std::thread::spawn(move || -> Result<(), addy::Error> {
        std::thread::sleep(std::time::Duration::from_secs(10));
        ctrl_c.default()?;
        Ok(())
    });

    /* Stop saying "Hello, World!" on resize after 5 seconds */
    std::thread::spawn(move || -> Result<(), addy::Error> {
        std::thread::sleep(std::time::Duration::from_secs(5));
        addy::mediate(SIGWINCH).remove("hello")?;
        Ok(())
    });

    /* Capture the input so we don't exit the program immediately */
    let mut buffer = [0; 1];
    loop {
        stdin().read(&mut buffer);
    }

    Ok(())
}

Signals Supported

Not all signals are supported on all platforms/architectures. Which signals does your platform support? Run: kill -l to find out!

  • SIGHUP
  • SIGINT
  • SIGQUIT
  • SIGILL
  • SIGTRAP
  • SIGABRT
  • SIGBUS
  • SIGFPE
  • SIGKILL
  • SIGUSR1
  • SIGSEGV
  • SIGUSR2
  • SIGPIPE
  • SIGALRM
  • SIGTERM
  • SIGSTKF
  • SIGCHLD
  • SIGCONT
  • SIGSTOP
  • SIGTSTP
  • SIGTTIN
  • SIGTTOU
  • SIGURG
  • SIGXCPU
  • SIGXFSZ
  • SIGVTAL
  • SIGPROF
  • SIGWINC
  • SIGIO
  • SIGPWR
  • SIGSYS
  • SIGEMT
  • SIGINFO

Errors

If the MPSC channel closes, of the Event Loop thread closes, there is no way to recover and any future Addy calls will return an addy::Error.

Dependencies

~70KB