#thread #parallel

status_executor

Run your work on some context (thread) and get status info back

1 unstable release

0.1.0 Dec 20, 2023

#788 in Concurrency

MIT/Apache

19KB
250 lines

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.

Dependencies

~0–265KB