#polling #task #poll #background-thread

interruptible_polling

General purpose polling tasks with RAII-driven fast clean exits

3 releases (breaking)

0.2.0 Apr 11, 2024
0.1.0 Jul 22, 2023
0.0.1 Jul 19, 2023

#6 in #polling

Download history 1/week @ 2024-07-23 1/week @ 2024-07-30 1/week @ 2024-09-17 7/week @ 2024-09-24

147 downloads per month
Used in cache_browns

MIT/Apache

24KB
351 lines

interruptible-polling

General purpose polling tasks with RAII-driven fast clean exits.

See docs.rs for full details.

License

This project is licensed under either of

at your option.

The SPDX license identifier for this project is MIT OR Apache-2.0.


lib.rs:

githubcrates-iodocs-rs


This library provides PollingTask and SelfUpdatingPollingTask structs for scheduling a closure to execute as a recurring task.

It is common for a service to have long-lived polling operations for the life of the process. The intended use case is to offer a RAII container for a polled operation that will interrupt pending sleeps to allow a low-latency clean exit.

If the poll operation is still running, the task drop will join the background thread which will exit after the closure finishes.

Examples

  • Use PollingTask to emit a heart beat every 30 seconds.

    use interruptible_polling::PollingTask;
    use std::time::Duration;
    
    let task = PollingTask::new(Duration::from_secs(30), || {
        println!("BeatBeat");
    });
    
  • Some polled operations such as configuration updates contain the updated rate at which the service should continue to poll for future updates. The SelfUpdatingPollingTask passes a callback to the poll task that allows it to conveniently apply the new state to future polls.

    use interruptible_polling::SelfUpdatingPollingTask;
    use std::time::Duration;
    use serde_json::{Value, from_reader};
    use std::fs::File;
    use std::io::BufReader;
    
    let task = SelfUpdatingPollingTask::new(Duration::from_secs(30),
        move |interval: &mut Duration| {
            let file = File::open("app.config").unwrap();
            let reader = BufReader::new(file);
            let config: Value = from_reader(reader).expect("JSON was not well-formatted");
    
            // Do other work with config
    
            *interval = Duration::from_secs(config["pollingInterval"].as_u64().expect("Polling interval isn't u64 convertable"));
        }
    );
    
  • If your poll operation is long-lived or internally iterative, there are opportunities to assert if the task is still active to allow the blocked clean exit to occur faster. If you create the task with PollingTask::new_with_checker or SelfUpdatingPollingTask::new_with_checker your closure will receive a lookup function to peek if the managed task is still active.

 use interruptible_polling::PollingTask;
 use std::time::Duration;

 let task = PollingTask::new_with_checker(
     Duration::from_secs(30),
     |checker: &dyn Fn() -> bool|
 {
     let keys = vec![1 ,2, 3];

     for key in keys {
         // Early exit if signaled. The task will not poll again either way, but you have
         // returned control to the parent task earlier.
         if !checker() {
             break;
         }

         // Some long or potentially long operation such as a synchronous web request.
     }
 });

Fire and Forget

For convenience, if you also need to run polling threads that don't require clean exits, fire and forget can be enabled. This is gated behind feature fire-forget to encourage use of the primary abstractions. It's not hard to make a polling thread, so typical crate users are here for the clean exit constructs. However, some projects need both. If you need both, enable the feature to make both available. By default, it's disabled.

No runtime deps