Crate status_executor

Source
Expand description

Execute long running computational tasks, optionally transmitting a status back

§Example usage

    use status_executor::{StatusExecutor, StatusSender, StdContext};
    // status data need to be Send + 'static and also implement Clone
    #[derive(Debug,Clone)]
    struct MyStatus {
        progress: i32,
        msg: String,
    }
    // lets some heavy work instead of this
    let e = StatusExecutor::new(StdContext::default(), |s| {
        let mut p = 0;
        while p < 100 {
            std::thread::sleep(std::time::Duration::from_secs_f32(0.4));
            s.send(MyStatus {
                progress: p,
                msg: "Working...".to_string(),
            });
            p += 15;
        }
        // post that we are done!
        s.send(MyStatus {
            progress: p,
            msg: "Done!".to_string(),
        });
        p
    });
    // your gui or whatever might grab the most recent (or none, if none is available) status
    while !e.is_done() {
        match e.status() {
            Some(s) => println!("{} - currently at {}", s.msg, s.progress),
            None => std::thread::yield_now(),
        }
    }
    // the most recent status is also saved even if nothing new is produced.
    match e.latest_status() {
         Some(s) => println!("{} - last at {}", s.msg, s.progress),
         None => {
             assert!(false, "We produced *at least* one status, this should ont happen");
         }
    }
    // result is an Option<Arc<T>> because otherwise you would have to either move it out of the internal state
    // or track the locking used for the internal state
    //let res = e.result().expect("function returned is_done() == true, this should exist now");
    // you can, however just take the result as well, consuming the executor.
    let res = e.take_result();
    assert_eq!(res,Some(105));

§Motivation

Sometimes the need arises to offload computation tasks. Async does not handle this well, and tokio etc. give you specific contexts and thread pools to run computation tasks on instead. And, as of writing this note, outside of async contexts, there isn’t really anything to just ease these type of fire-off-once takes-forever tasks.

status_executor provides two Structs - Executor and StatusExecutor for this task. Executor itself is nothing but a overblown join handle. Using it becomes interesting if you enable the rayon feature. Then you can call Executor::new using RayonContext instead, which makes the work spwan in rayons global pool instead.

StatusExecutor is the name giving part. Say, you have a gui application, and you are doing heavy lifting in the background. Users might be interested in the current state (it might, after all, take 20 minutes or so). The obvious Solution is a channel. However, it becomes a bit tedious to write this rapidly. So StatusExecutor is a little helper for that.

§Panic behaviour

Threads running in an executor might panic If the rayon feature is used, rayon does have a panic handler that will usually lead to an abort (unless you modify the thread pools panic handler) Mirroring this, the StdContext will abort the process Using RayonGlobalContext or RayonContext will instead just use the set panic handler - which might be a panic as well.

Structs§

Executor
An Executor runs a function in some Context (this will usually be a thread)
StatusExecutor
A StatusExecutor runs a function in some Context (this will usually be a thread)
StatusSender
A convenience struct around the channel used to send a status S from the worker to the StatusExecutor
StdContext
An executor used to run functions on std::thread::spawn() instances. You can just Default this one, right now it has no state

Traits§

Context
Trait used for execution contexts