# curfew
[](https://crates.io/crates/curfew)
[](https://docs.rs/curfew)
[](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:
```toml
[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
```rust
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
```rust
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)
```rust
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](./_dev/PRD.md) for the full product specification and
[CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
## License
This project is licensed under MIT. See [LICENSE](LICENSE) for details.