curfew 0.0.5

A deadline-aware fork-join model where multiple tasks run in parallel, but only those that finish before a specified absolute clock time are joined and their results collected.
Documentation

curfew

Crates.io Documentation License

curfew is a deadline-aware fork-join helper for Rust. It lets you spin up work across multiple threads, set an absolute deadline (std::time::Instant), and collect only the results that arrive on time. Late workers are skipped without attempting to cancel them, so real-time sections of your application can keep moving.

Installation

Add the crate to your Cargo manifest:

[dependencies]
curfew = "0.0.5"

Enable the optional tokio feature to work with async tasks powered by Tokio.

Usage

Joining a group of tasks before a deadline

use curfew::{DeadlineJoiner, TaskOutcome};
use std::thread;
use std::time::{Duration, Instant};

fn main() {
    let deadline = Instant::now() + Duration::from_millis(200);
    let mut joiner = DeadlineJoiner::new(deadline);

    for job in 0..4 {
        joiner.spawn(move || {
            // staggered work with different run times
            thread::sleep(Duration::from_millis(40 * job));
            job * 10
        });
    }

    let summary = joiner.join_ready();
    println!("completed before deadline: {}", summary.completed_count());
    println!("missed deadline: {}", summary.timed_out_count());

    for task in summary.completed {
        match task.outcome {
            TaskOutcome::Completed(value) => println!("task {} => {value}", task.task_id),
            TaskOutcome::Panicked(info) => println!("task {} panicked: {}", task.task_id, info),
        }
    }
}

Joining an individual handle with a deadline

use curfew::{join_with_deadline, JoinOutcome, TaskOutcome};
use std::thread;
use std::time::{Duration, Instant};

fn main() {
    let handle = thread::spawn(|| {
        thread::sleep(Duration::from_millis(150));
        99
    });

    match join_with_deadline(handle, Instant::now() + Duration::from_millis(80)) {
        JoinOutcome::Completed(result) => match result.outcome {
            TaskOutcome::Completed(value) => println!("finished on time: {value}"),
            TaskOutcome::Panicked(info) => println!("task panicked: {info}"),
        },
        JoinOutcome::TimedOut(late) => {
            println!("missed deadline; waiting asynchronously for the late result");
            let result = late.wait();
            println!("late completion: {:?}", result.outcome);
        }
    }
}

Async tasks with Tokio (tokio feature)

use curfew::{
    join_with_deadline_async, DeadlineJoinerAsync, JoinOutcomeAsync, TaskOutcome,
};
use std::time::{Duration, Instant};

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let deadline = Instant::now() + Duration::from_millis(120);
    let mut joiner = DeadlineJoinerAsync::new(deadline);

    for value in 0..3u32 {
        joiner.spawn(async move {
            tokio::time::sleep(Duration::from_millis(u64::from(30 * (value + 1))))
                .await;
            value
        });
    }

    let summary = joiner.join_ready().await;
    println!("async tasks finished on time: {}", summary.completed_count());

    let future = async {
        tokio::time::sleep(Duration::from_millis(200)).await;
        99
    };

    match join_with_deadline_async(future, Instant::now() + Duration::from_millis(60)).await {
        JoinOutcomeAsync::Completed(result) => match result.outcome {
            TaskOutcome::Completed(value) => println!("completed on time: {value}"),
            TaskOutcome::Panicked(info) => println!("task panicked: {info}"),
        },
        JoinOutcomeAsync::TimedOut(late) => {
            println!("future missed the deadline; waiting for late completion");
            let result = late.wait().await;
            println!("late outcome: {:?}", result.outcome);
        }
    }
}

API Highlights

  • DeadlineJoiner spawns tasks, tracks their start/finish instants, and only returns results that beat the deadline.
  • join_with_deadline wraps a single JoinHandle, returning a LateTask so callers can optionally await the late result later.
  • Enable the tokio feature for async siblings DeadlineJoinerAsync and join_with_deadline_async with the same semantics.
  • Rich metadata via TaskResult, TaskOutcome, and TimedOutTask for diagnostics and logging.

Limitations

  • Tasks are never forcefully terminated. Anything that misses the deadline continues running in the background until it completes naturally.
  • Deadlines rely on the monotonic Instant clock. System clock adjustments do not influence scheduling, but very long sleeps may still drift.

Examples

Run the bundled examples with cargo run --example basic or see examples/ for more scenarios (enable --features tokio for async_example).

Development

See _dev/PRD.md for the full product specification and CONTRIBUTING.md for contribution guidelines.

License

This project is licensed under MIT. See LICENSE for details.