Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,33 @@ jobs:
command: test
args: --verbose

msrv:
name: MSRV
runs-on: ubuntu-latest
steps:
- name: Setup Rust 1.94.1
uses: actions-rs/toolchain@v1
with:
toolchain: 1.94.1
override: true
- name: Checkout sources
uses: actions/checkout@v2
- name: Run tests (default features)
uses: actions-rs/cargo@v1
with:
command: test
args: --verbose
- name: Run tests (fnv)
uses: actions-rs/cargo@v1
with:
command: test
args: --verbose --no-default-features --features fnv-hash,uuid-extras
- name: Run tests (sip)
uses: actions-rs/cargo@v1
with:
command: test
args: --verbose --no-default-features --features sip-hash,uuid-extras

no-default-feature-tests:
runs-on: ${{ matrix.os }}
strategy:
Expand Down
14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ name = "hierarchical_hash_wheel_timer"
version = "1.3.0"
authors = ["Lars Kroll <bathtor@googlemail.com>"]
edition = "2021"
description = "A low-level timer implementantion using a hierarchical four-level hash wheel with overflow."
rust-version = "1.94.1"
description = "A low-level timer implementation using a hierarchical four-level hash wheel with overflow."
documentation = "https://docs.rs/hierarchical_hash_wheel_timer"
homepage = "https://github.com/Bathtor/rust-hash-wheel-timer"
repository = "https://github.com/Bathtor/rust-hash-wheel-timer"
Expand All @@ -16,7 +17,7 @@ license = "MIT"
default = ["uuid-extras", "fx-hash", "thread-timer"]
uuid-extras = ["uuid"]
thread-timer = ["crossbeam-channel"]
# The following three features are mutually exclusive and you have to pick exactly one of them!
# The following three features are mutually exclusive and you have to pick exactly one of them.

# Use Rust's default Sip Hasher for cancellable timers
sip-hash = []
Expand All @@ -26,8 +27,8 @@ fnv-hash = ["fnv"]
fx-hash = ["rustc-hash"]

[dependencies]
uuid = { version = "1.3", features = ["v4"], optional = true }
fnv = { version = "1.0", optional = true }
uuid = { version = "1", features = ["v4"], optional = true }
fnv = { version = "1", optional = true }
rustc-hash = { version = "2", optional = true }
crossbeam-channel = { version = "0.5", optional = true }

Expand All @@ -40,9 +41,8 @@ maintenance = { status = "passively-maintained" }
github-actions = { repository = "Bathtor/rust-hash-wheel-timer", workflow = "ci.yml" }

[dev-dependencies]
criterion = "0.5"
rand = "0.8"
rand_xoshiro = "0.6"
criterion = "0.8"
rand = "0.10"

[[bench]]
name = "wheel_benchmark"
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This crate provides a low-level event timer implementation based on hierarchical
The APIs in the crate are offered at three different levels of abstraction, listed below from lowest to highest.

### 1 – Single Wheel
The fundamental abstraction of this crate a single hash wheel with 256 slots addressed with a single byte. Each slot stores a list of a generic event type.
The fundamental abstraction of this crate is a single hash wheel with 256 slots addressed with a single byte. Each slot stores a list of a generic event type.
The whole wheel can be "ticked" causing entries in the slots that are being moved over to expire. With every tick, all expired event entries are returned for handling.

### 2 – Hierarchical Wheel
Expand All @@ -26,6 +26,8 @@ This crate provides two variant implementations of this four level wheel structu
- The `wheels::quad_wheel::QuadWheelWithOverflow` corresponds directly to the implementation described above.
- The `wheels::cancellable::QuadWheelWithOverflow` additionally supports the cancellation of outstanding timers before they expire. In order to do so, however, it requires the generic timer entry type to provide a unique identifier field. It also uses `std::rc::Rc` internally to avoid double storing the actual entry, which makes it (potentially) unsuitable for situations where the timer must be able to move between threads.

For the cancellable wheel, exactly one of `fx-hash`, `fnv-hash`, or `sip-hash` must be enabled.

### 3 – High Level APIs
This crate also provides three high-level APIs that can either be used directly or can be seen as examples of how to use the lower level APIs in an application.

Expand All @@ -44,7 +46,7 @@ The `manual_timer` module provides the same queue-based scheduling API as the th

## Documentation

For reference and examples check the [API Docs](https://docs.rs/hierarchical_hash_wheel_timer).
For reference and examples, check the [API docs](https://docs.rs/hierarchical_hash_wheel_timer).

## Performance

Expand All @@ -54,6 +56,6 @@ You can repeat these experiments on your own hardware by checking out the source

## License

Licensed under the terms of MIT license.
Licensed under the terms of the MIT licence.

See [LICENSE](LICENSE) for details.
10 changes: 5 additions & 5 deletions benches/wheel_benchmark.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use criterion::{criterion_group, criterion_main, BatchSize, Bencher, Criterion, Throughput};
use hierarchical_hash_wheel_timer::{wheels::cancellable::*, UuidOnlyTimerEntry};
use rand::prelude::*;
use rand::{rngs::Xoshiro256PlusPlus, RngExt, SeedableRng};
use std::{rc::Rc, time::Duration};
use uuid::Uuid;

Expand Down Expand Up @@ -53,10 +53,10 @@ fn write_only_uniform_bench(bencher: &mut Bencher) {
|| {
let timer: QuadWheelWithOverflow<UuidOnlyTimerEntry> = QuadWheelWithOverflow::new();
let mut entries = Vec::with_capacity(NUM_ELEMENTS);
let mut rng = rand_xoshiro::Xoshiro256PlusPlus::seed_from_u64(42);
let mut rng = Xoshiro256PlusPlus::seed_from_u64(42);
for _i in 1..=NUM_ELEMENTS {
let id = Uuid::new_v4();
let mut delay: u32 = rng.gen();
let mut delay: u32 = rng.random();
if delay == 0 {
// make sure the entry is actually inserted and not just returned immediately
delay = 1;
Expand Down Expand Up @@ -85,10 +85,10 @@ fn write_only_uniform_with_overflow_bench(bencher: &mut Bencher) {
|| {
let timer: QuadWheelWithOverflow<UuidOnlyTimerEntry> = QuadWheelWithOverflow::new();
let mut entries = Vec::with_capacity(NUM_ELEMENTS);
let mut rng = rand_xoshiro::Xoshiro256PlusPlus::seed_from_u64(42);
let mut rng = Xoshiro256PlusPlus::seed_from_u64(42);
for _i in 1..=NUM_ELEMENTS {
let id = Uuid::new_v4();
let mut delay: u64 = rng.gen();
let mut delay: u64 = rng.random();
if delay == 0 {
// make sure the entry is actually inserted and not just returned immediately
delay = 1;
Expand Down
57 changes: 7 additions & 50 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,56 +1,13 @@
max_width = 100
hard_tabs = false
tab_spaces = 4
newline_style = "Unix"
use_small_heuristics = "Default"
indent_style = "Block"
wrap_comments = false
comment_width = 180
normalize_comments = false
#license_template_path = ""
format_strings = false
empty_item_single_line = true
struct_lit_single_line = true
fn_single_line = false
where_single_line = false
edition = "2021"

imports_granularity = "Crate"
imports_indent = "Block"
imports_layout = "HorizontalVertical"
reorder_imports = true
reorder_modules = true
reorder_impl_items = true
type_punctuation_density = "Wide"
space_before_colon = false
space_after_colon = true
spaces_around_ranges = false
binop_separator = "Front"
remove_nested_parens = true
combine_control_expr = true
struct_field_align_threshold = 0
match_arm_blocks = true
force_multiline_blocks = false

fn_params_layout = "Tall"
brace_style = "SameLineWhere"
control_brace_style = "AlwaysSameLine"
trailing_semicolon = true
trailing_comma = "Vertical"
match_block_trailing_comma = false
blank_lines_upper_bound = 1
blank_lines_lower_bound = 0
merge_derives = true
use_try_shorthand = false
binop_separator = "Front"

use_field_init_shorthand = false
force_explicit_abi = true
condense_wildcard_suffixes = false
color = "Auto"
required_version = "1.9.0"
unstable_features = false
disable_all_formatting = false
skip_children = false
show_parse_errors = true
error_on_line_overflow = false
error_on_unformatted = false
ignore = []
emit_mode = "Files"
make_backup = false
edition = "2018"
reorder_impl_items = false
wrap_comments = false
27 changes: 14 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
//! listed below from lowest to highest.
//!
//! # 1 – Single Wheel
//! The fundamental abstraction of this crate a single hash wheel with 256 slots
//! The fundamental abstraction of this crate is a single hash wheel with 256 slots
//! addressed with a single byte. Each slot stores a list of a generic event type.
//! The whole wheel can be "ticked" causing entries in the slots that are being moved over
//! to expire. With every tick, all expired event entries are returned for handling.
//! For more details see the [byte_wheel](wheels::byte_wheel) module.
//!
//! # 2 – Hierachical Wheel
//! Combining four byte wheels we get a hierachical timer that can represent timeouts
//! # 2 – Hierarchical Wheel
//! Combining four byte wheels we get a hierarchical timer that can represent timeouts
//! up to [`u32::MAX`](std::u32::MAX) time units into the future.
//! In order to support timeouts of up to [`u64::MAX`](std::u64::MAX) time units,
//! our implementations also come with an overflow list, which stores all timers that didn't fit
Expand All @@ -28,8 +28,9 @@
//! - The [quad_wheel::QuadWheelWithOverflow](wheels::quad_wheel::QuadWheelWithOverflow) corresponds directly to the implementation described above.
//! - The [cancellable::QuadWheelWithOverflow](wheels::cancellable::QuadWheelWithOverflow) additionally supports the cancellation of outstanding timers
//! before they expire. In order to do so, however, it requires the generic timer entry type to provide a unique identifier field. It also uses
//! [Rc](std::rc::Rc) internally to avoid double storing the actual entry, which makes it (potentialy) unsuitable for situations where the timer must
//! be able to move threads (since Rc](std::rc::Rc) is not `Send`).
//! [Rc](std::rc::Rc) internally to avoid double storing the actual entry, which makes it potentially unsuitable for situations where the timer must
//! be able to move between threads (since [Rc](std::rc::Rc) is not `Send`).
//! - Exactly one of `fx-hash`, `fnv-hash`, or `sip-hash` must be enabled for this variant.
//!
//! # 3 – High Level APIs
//! This crate also provides three high-level APIs that can either be used
Expand All @@ -41,16 +42,16 @@
//! and discarded once expired.
//!
//! ## Simulation Timer
//! The [simulation](simulation) module provides an implementation for an event timer used to drive a discrete event simulation.
//! Its particular feature is that it can skip quickly through periods where no events are schedulled as it doesn't track real time,
//! The [simulation] module provides an implementation for an event timer used to drive a discrete event simulation.
//! Its particular feature is that it can skip quickly through periods where no events are scheduled as it doesn't track real time,
//! but rather provides the rate at which the simulation proceeds.
//!
//! ## Thread Timer
//! The [thread_timer](thread_timer) module provides a timer for real-time event schedulling with millisecond accuracy.
//! The [thread_timer] module provides a timer for real-time event scheduling with millisecond accuracy.
//! It runs on its own dedicated thread and uses a shareable handle called a `TimerRef` for communication with other threads.
//!
//! ## Manual Timer
//! The [manual_timer](manual_timer) module provides the same queue-based
//! The [manual_timer] module provides the same queue-based
//! scheduling API as the thread timer, but advances only when the caller
//! explicitly steps time forward.

Expand All @@ -77,12 +78,12 @@ mod uuid_extras;
#[cfg(feature = "uuid-extras")]
pub use self::uuid_extras::*;

/// Errors encounted by a timer implementation
/// Errors encountered by a timer implementation
#[derive(Debug)]
pub enum TimerError<EntryType> {
/// The timeout with the given id was not found
NotFound,
/// The timout has already expired
/// The timeout has already expired
Expired(EntryType),
}

Expand All @@ -91,7 +92,7 @@ pub enum TimerError<EntryType> {
pub struct IdOnlyTimerEntry<I> {
/// The unique identifier part of the entry
pub id: I,
/// The delay that this entry is to be schedulled with (i.e., expire after)
/// The delay that this entry is to be scheduled with (i.e., expire after)
pub delay: Duration,
}
impl<I> IdOnlyTimerEntry<I> {
Expand Down Expand Up @@ -120,7 +121,7 @@ where
}
}

/// A module with some convenince functions for writing timer tests
/// A module with some convenience functions for writing timer tests
#[cfg(test)]
pub mod test_helpers {
use std::time::Duration;
Expand Down
2 changes: 1 addition & 1 deletion src/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! but rather provides the rate at which the simulation proceeds.
//!
//! Progress in the simulation is driven by repeatedly calling the [next](SimulationTimer::next) function
//! until it returns [SimulationStep::Finished](SimulationStep::Finished) indicating that the timer is empty
//! until it returns [SimulationStep::Finished] indicating that the timer is empty
//! and thus the simulation has run to completion.
//!
//! # Example
Expand Down
2 changes: 1 addition & 1 deletion src/thread_timer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! This module provides a timer for real-time event schedulling with millisecond accuracy.
//!
//! It runs on its own dedicated thread and uses a shareable handle called a `TimerRef` for communication with other threads.
//! This inter-thread communication is based on [crossbeam_channel](crossbeam_channel).
//! This inter-thread communication is based on [crossbeam_channel].
//!
//! ## Note
//! Sine this timer runs on its own thread, instance creation will fail if the generic id or state types used are not `Send`.
Expand Down
2 changes: 1 addition & 1 deletion src/uuid_extras.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use uuid::Uuid;
pub type UuidOnlyTimerEntry = IdOnlyTimerEntry<Uuid>;

impl UuidOnlyTimerEntry {
/// Produce an entry with a random [Uuid](uuid::Uuid) and the given `delay`
/// Produce an entry with a random [Uuid] and the given `delay`
///
/// Uses `Uuid::new_v4()` internally.
pub fn with_random_id(delay: Duration) -> Self {
Expand Down
Loading
Loading