Skip to main content

ThreadpoolPeriodicTimer

Struct ThreadpoolPeriodicTimer 

Source
pub struct ThreadpoolPeriodicTimer { /* private fields */ }
Expand description

An owned repeating thread-pool timer.

The period is fixed when the timer is created, so the type says what it is: this object exists to tick on a cadence, and there is no argument that can quietly turn it into a one-shot.

§Ticks can overlap

The pool queues each tick on schedule regardless of whether the previous tick has finished. If the callback takes longer than the period, two or more runs of it will execute concurrently on different pool threads. This is the property that makes periodic timers surprising in practice, and it follows from the cadence being fixed: the schedule cannot wait for the callback without ceasing to be a schedule.

So a ThreadpoolPeriodicTimer callback must be safe to run concurrently with itself. If that is awkward, the alternative is a one-shot ThreadpoolTimer re-armed from inside its own callback with crate::timer::TimerFiring::rearm_after: there is never more than one firing outstanding, and the gap is measured from the end of each firing rather than from a fixed schedule.

ThreadpoolPeriodicTimerThreadpoolTimer + rearm_after
Cadencefixed, independent of callback durationdrifts by the callback duration
Concurrent runs of the callbackpossiblenever
Slow callbackticks pile up and overlapnext tick simply happens later

§Teardown

Drop stops the timer before draining callbacks, so it cannot requeue during teardown. ThreadpoolPeriodicTimer::stop_and_drain does the same thing under the caller’s control, and is the ordering to copy if doing it by hand: stop first, drain second.

§Examples

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use windows_threadpool_sys::timer::ThreadpoolPeriodicTimer;

let ticks = Arc::new(AtomicUsize::new(0));
let counter = Arc::clone(&ticks);

// The period belongs to the timer, not to a call.
let timer = ThreadpoolPeriodicTimer::new(Duration::from_millis(1), move |_tick| {
    counter.fetch_add(1, Ordering::SeqCst);
}, None)?;

timer.start();
while ticks.load(Ordering::SeqCst) < 3 {
    std::thread::yield_now();
}

timer.stop_and_drain();
assert!(ticks.load(Ordering::SeqCst) >= 3);

Stopping from inside the callback, for “tick until done”. Note the counter may pass the threshold, because a tick already queued still runs:

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use windows_threadpool_sys::timer::ThreadpoolPeriodicTimer;

let ticks = Arc::new(AtomicUsize::new(0));
let counter = Arc::clone(&ticks);

let timer = ThreadpoolPeriodicTimer::new(Duration::from_millis(1), move |tick| {
    if counter.fetch_add(1, Ordering::SeqCst) >= 2 {
        tick.stop();
    }
}, None)?;

timer.start();
while timer.is_running() {
    std::thread::yield_now();
}
timer.stop_and_drain();
assert!(ticks.load(Ordering::SeqCst) >= 3);

Implementations§

Source§

impl ThreadpoolPeriodicTimer

Source

pub const MIN_PERIOD: Duration

The shortest period this timer can express: one millisecond.

SetThreadpoolTimer takes the period as whole milliseconds, so nothing shorter can be represented. This is a floor on what may be asked for, not a promise about delivery: ticks arrive on the system timer tick, which is far coarser (~15.6ms by default).

Source

pub const MAX_PERIOD: Duration

The longest period this timer can express: u32::MAX milliseconds, or just under 50 days.

The pool’s period field is a u32 count of milliseconds, so a longer period cannot be represented.

Source

pub fn new<F>( period: Duration, callback: F, env: Option<&mut CallbackEnviron<'_>>, ) -> Result<Self>
where F: Fn(&PeriodicTick<'_>) + Send + Sync + 'static,

Create a stopped timer that invokes callback every period.

period must be a whole number of milliseconds between MIN_PERIOD and MAX_PERIOD; anything else is rejected. The pool takes the period as a u32 count of milliseconds, so every other value would be quietly altered to fit:

  • a period below a millisecond rounds down to zero, and a zero period means “do not repeat” – the timer would fire once and stop, which is a ThreadpoolPeriodicTimer silently behaving like a ThreadpoolTimer;
  • a fractional period such as 1.5ms is truncated, so the timer would tick at 1ms while period still reported 1.5ms;
  • a period beyond u32::MAX milliseconds would be capped, ticking far more often than asked.

Rejecting rather than rounding keeps period an accurate report of what was scheduled.

Pass Some(env) to select a private pool or callback priority; None uses the process-default pool with default priority.

The callback runs on a shared, process-managed pool thread, may run concurrently with itself (see the type documentation), must restore any thread state it changes, and must not terminate its thread. It must not panic: a panic unwinds to the extern "system" trampoline and aborts the process.

§Errors

Returns io::ErrorKind::InvalidInput if period is outside MIN_PERIOD..=MAX_PERIOD or is not a whole number of milliseconds, or the error from CreateThreadpoolTimer.

Source

pub fn period(&self) -> Duration

The period this timer ticks on.

Source

pub fn start(&self)

Start ticking, with the first tick one period from now.

Source

pub fn start_after(&self, first_delay: Duration)

Start ticking, with the first tick first_delay from now.

Subsequent ticks follow every ThreadpoolPeriodicTimer::period. A zero first_delay makes the first tick due immediately.

Source

pub fn start_at(&self, when: SystemTime)

Start ticking, with the first tick at the wall-clock instant when.

Unlike a relative first delay, an absolute one passes through sleep and hibernation.

Source

pub fn start_with_window(&self, first_delay: Duration, window: Duration)

Start ticking, allowing the system a coalescing window on each tick.

window is the tolerance the system may add so it can group this timer with other expirations and wake the processor less often, trading timing precision for power.

Source

pub fn stop(&self)

Stop the timer.

Future ticks stop being queued, but a tick already queued still runs and ticks already executing are unaffected. Use ThreadpoolPeriodicTimer::stop_and_drain to also wait for those.

Source

pub fn is_running(&self) -> bool

Whether the timer is currently started.

Ticking does not clear the schedule, so this stays true until something stops the timer – ThreadpoolPeriodicTimer::stop, PeriodicTick::stop, or teardown.

Source

pub fn wait(&self)

Block until all queued and executing ticks have completed.

Stop the timer first, or this waits for a schedule that keeps producing new ticks. ThreadpoolPeriodicTimer::stop_and_drain does both in the right order.

Source

pub fn stop_and_drain(&self)

Stop the timer and wait until no tick is queued or executing.

This is the correct teardown order – stop first, drain second – and is what Drop performs. Ticks that have not started are dropped rather than run.

The result holds provided no other thread starts the timer during the call. ThreadpoolPeriodicTimer is Sync and the start* methods take &self, so a start landing between the stop and the drain is not excluded by anything here, and would leave a schedule installed on return. A caller needing the timer to be provably stopped must own it exclusively or serialize access to it. Unlike the one-shot timer there is no re-arm to suppress: PeriodicTick::stop only ever stops.

Trait Implementations§

Source§

impl Debug for ThreadpoolPeriodicTimer

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for ThreadpoolPeriodicTimer

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
Source§

impl Send for ThreadpoolPeriodicTimer

Source§

impl Sync for ThreadpoolPeriodicTimer

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.