Skip to content

Commit

Permalink
Rework timer event sources around Poll
Browse files Browse the repository at this point in the history
The reworks the `Timer` event source to have it directly driven
by the core polling of the event loop, instead of relying on
external timing mechanisms (like a thread or a timerfd).

Fixes #9
Fixes #83
Fixes #84
  • Loading branch information
elinorbgr authored and vberger committed Mar 30, 2022
1 parent 2c40aec commit 7835e53
Show file tree
Hide file tree
Showing 19 changed files with 825 additions and 970 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@
associated `Error` type on the `EventSource` trait.
- **Breaking:** Many API functions now use Calloop's own error type (`calloop::Error`) instead of
`std::io::Error` as the error variants of their returned results.
- On Linux `Timer<T>` is now driven by `timerfd`.
- **Breaking:** The `Timer` event source has been completely reworked and is now directly driven by
calloop polling mechanism instead of a background thread. Timer multiplexing is now handled by
creating multiple `Timer`s, and self-repeating timers is handled by the return value of the
associated event callback.
- **Breaking:** The minimum supported Rust version is now 1.53.0
- Introduce `EventLoop::try_new_high_precision()` for sub-millisecond accuracy in the event loop

## 0.9.2 -- 2021-12-27

Expand Down
26 changes: 11 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ For simple uses, you can just add event sources with callbacks to the event
loop. For example, here's a runnable program that exits after five seconds:

```rust
use calloop::{timer::Timer, EventLoop, LoopSignal};
use calloop::{timer::{Timer, TimeoutAction}, EventLoop, LoopSignal};

fn main() {
// Create the event loop. The loop is parameterised by the kind of shared
Expand All @@ -43,18 +43,8 @@ fn main() {
// callbacks.
let handle = event_loop.handle();

// Create our event source, a timer. Note that this is also parameterised by
// the data for the events it generates. We've let Rust infer that here.
let source = Timer::new().expect("Failed to create timer event source!");

// Most event source APIs provide two things: an event source to go into the
// event loop, and some way of triggering that source from elsewhere. In
// this case, we use a handle to the timer to set timeouts.
//
// Note that this can go before or after the call to insert_source(), and
// even inside another event callback.
let timer_handle = source.handle();
timer_handle.add_timeout(std::time::Duration::from_secs(5), "Timeout reached!");
// Create our event source, a timer, that will expire in 2 seconds
let source = Timer::from_duration(std::time::Duration::from_secs(2));

// Inserting an event source takes this general form. It can also be done
// from within the callback of another event source.
Expand All @@ -65,16 +55,22 @@ fn main() {
// a callback that is invoked whenever this source generates an event
|event, _metadata, shared_data| {
// This callback is given 3 values:
// - the event generated by the source (in our case, a string slice)
// - the event generated by the source (in our case, timer events are the Instant
// representing the deadline for which it has fired)
// - &mut access to some metadata, specific to the event source (in our case, a
// timer handle)
// - &mut access to the global shared data that was passed to EventLoop::run or
// EventLoop::dispatch (in our case, a LoopSignal object to stop the loop)
//
// The return type is just () because nothing uses it. Some
// sources will expect a Result of some kind instead.
println!("Event fired: {}", event);
println!("Timeout for {:?} expired!", event);
// notify the event loop to stop running using the signal in the shared data
// (see below)
shared_data.stop();
// The timer event source requires us to return a TimeoutAction to
// specify if the timer should be rescheduled. In our case we just drop it.
TimeoutAction::Drop
},
)
.expect("Failed to insert event source!");
Expand Down
30 changes: 30 additions & 0 deletions examples/high_precision.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use std::time::{Duration, Instant};

use calloop::{
timer::{TimeoutAction, Timer},
EventLoop,
};

fn main() {
let mut event_loop =
EventLoop::try_new_high_precision().expect("Failed to initialize the event loop!");

let before = Instant::now();

event_loop
.handle()
.insert_source(
Timer::from_duration(Duration::from_micros(20)),
|_, _, _| TimeoutAction::Drop,
)
.unwrap();

event_loop.dispatch(None, &mut ()).unwrap();

let elapsed = before.elapsed();

println!(
"The event loop slept for {} microseconds.",
elapsed.as_micros()
);
}
29 changes: 14 additions & 15 deletions examples/timer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use calloop::{timer::Timer, EventLoop, LoopSignal};
use calloop::{
timer::{TimeoutAction, Timer},
EventLoop, LoopSignal,
};

fn main() {
// Create the event loop. The loop is parameterised by the kind of shared
Expand All @@ -15,18 +18,8 @@ fn main() {
// callbacks.
let handle = event_loop.handle();

// Create our event source, a timer. Note that this is also parameterised by
// the data for the events it generates. We've let Rust infer that here.
let source = Timer::new().expect("Failed to create timer event source!");

// Most event source APIs provide two things: an event source to go into the
// event loop, and some way of triggering that source from elsewhere. In
// this case, we use a handle to the timer to set timeouts.
//
// Note that this can go before or after the call to insert_source(), and
// even inside another event callback.
let timer_handle = source.handle();
timer_handle.add_timeout(std::time::Duration::from_secs(1), "Timeout reached!");
// Create our event source, a timer, that will expire in 2 seconds
let source = Timer::from_duration(std::time::Duration::from_secs(2));

// Inserting an event source takes this general form. It can also be done
// from within the callback of another event source.
Expand All @@ -37,16 +30,22 @@ fn main() {
// a callback that is invoked whenever this source generates an event
|event, _metadata, shared_data| {
// This callback is given 3 values:
// - the event generated by the source (in our case, a string slice)
// - the event generated by the source (in our case, timer events are the Instant
// representing the deadline for which it has fired)
// - &mut access to some metadata, specific to the event source (in our case, a
// timer handle)
// - &mut access to the global shared data that was passed to EventLoop::run or
// EventLoop::dispatch (in our case, a LoopSignal object to stop the loop)
//
// The return type is just () because nothing uses it. Some
// sources will expect a Result of some kind instead.
println!("Event fired: {}", event);
println!("Timeout for {:?} expired!", event);
// notify the event loop to stop running using the signal in the shared data
// (see below)
shared_data.stop();
// The timer event source requires us to return a TimeoutAction to
// specify if the timer should be rescheduled. In our case we just drop it.
TimeoutAction::Drop
},
)
.expect("Failed to insert event source!");
Expand Down
26 changes: 11 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
//! loop. For example, here's a runnable program that exits after five seconds:
//!
//! ```no_run
//! use calloop::{timer::Timer, EventLoop, LoopSignal};
//! use calloop::{timer::{Timer, TimeoutAction}, EventLoop, LoopSignal};
//!
//! fn main() {
//! // Create the event loop. The loop is parameterised by the kind of shared
Expand All @@ -37,18 +37,8 @@
//! // callbacks.
//! let handle = event_loop.handle();
//!
//! // Create our event source, a timer. Note that this is also parameterised by
//! // the data for the events it generates. We've let Rust infer that here.
//! let source = Timer::new().expect("Failed to create timer event source!");
//!
//! // Most event source APIs provide two things: an event source to go into the
//! // event loop, and some way of triggering that source from elsewhere. In
//! // this case, we use a handle to the timer to set timeouts.
//! //
//! // Note that this can go before or after the call to insert_source(), and
//! // even inside another event callback.
//! let timer_handle = source.handle();
//! timer_handle.add_timeout(std::time::Duration::from_secs(5), "Timeout reached!");
//! // Create our event source, a timer, that will expire in 2 seconds
//! let source = Timer::from_duration(std::time::Duration::from_secs(2));
//!
//! // Inserting an event source takes this general form. It can also be done
//! // from within the callback of another event source.
Expand All @@ -59,16 +49,22 @@
//! // a callback that is invoked whenever this source generates an event
//! |event, _metadata, shared_data| {
//! // This callback is given 3 values:
//! // - the event generated by the source (in our case, a string slice)
//! // - the event generated by the source (in our case, timer events are the Instant
//! // representing the deadline for which it has fired)
//! // - &mut access to some metadata, specific to the event source (in our case, a
//! // timer handle)
//! // - &mut access to the global shared data that was passed to EventLoop::run or
//! // EventLoop::dispatch (in our case, a LoopSignal object to stop the loop)
//! //
//! // The return type is just () because nothing uses it. Some
//! // sources will expect a Result of some kind instead.
//! println!("Event fired: {}", event);
//! println!("Timeout for {:?} expired!", event);
//! // notify the event loop to stop running using the signal in the shared data
//! // (see below)
//! shared_data.stop();
//! // The timer event source requires us to return a TimeoutAction to
//! // specify if the timer should be rescheduled. In our case we just drop it.
//! TimeoutAction::Drop
//! },
//! )
//! .expect("Failed to insert event source!");
Expand Down
Loading

0 comments on commit 7835e53

Please sign in to comment.