kernel::time

Module hrtimer

Source
Expand description

Intrusive high resolution timers.

Allows running timer callbacks without doing allocations at the time of starting the timer. For now, only one timer per type is allowed.

§Vocabulary

States:

  • Stopped: initialized but not started, or cancelled, or not restarted.
  • Started: initialized and started or restarted.
  • Running: executing the callback.

Operations:

  • Start
  • Cancel
  • Restart

Events:

  • Expire

§State Diagram

                                                  Return NoRestart
                      +---------------------------------------------------------------------+
                      |                                                                     |
                      |                                                                     |
                      |                                                                     |
                      |                                         Return Restart              |
                      |                                      +------------------------+     |
                      |                                      |                        |     |
                      |                                      |                        |     |
                      v                                      v                        |     |
          +-----------------+      Start      +------------------+           +--------+-----+--+
          |                 +---------------->|                  |           |                 |
Init      |                 |                 |                  |  Expire   |                 |
--------->|    Stopped      |                 |      Started     +---------->|     Running     |
          |                 |     Cancel      |                  |           |                 |
          |                 |<----------------+                  |           |                 |
          +-----------------+                 +---------------+--+           +-----------------+
                                                    ^         |
                                                    |         |
                                                    +---------+
                                                     Restart

A timer is initialized in the stopped state. A stopped timer can be started by the start operation, with an expiry time. After the start operation, the timer is in the started state. When the timer expires, the timer enters the running state and the handler is executed. After the handler has returned, the timer may enter the *started or stopped state, depending on the return value of the handler. A timer in the started or running state may be canceled by the cancel operation. A timer that is cancelled enters the stopped state.

A cancel or restart operation on a timer in the running state takes effect after the handler has returned and the timer has transitioned out of the running state.

A restart operation on a timer in the stopped state is equivalent to a start operation.

When a type implements both HrTimerPointer and Clone, it is possible to issue the start operation while the timer is in the started state. In this case the start operation is equivalent to the restart operation.

§Examples

§Using an intrusive timer living in a Box


#[pin_data]
struct Shared {
    #[pin]
    flag: Atomic<u64>,
    #[pin]
    cond: Completion,
}

impl Shared {
    fn new() -> impl PinInit<Self> {
        pin_init!(Self {
            flag <- Atomic::new(0),
            cond <- Completion::new(),
        })
    }
}

#[pin_data]
struct BoxIntrusiveHrTimer {
    #[pin]
    timer: HrTimer<Self>,
    shared: Arc<Shared>,
}

impl BoxIntrusiveHrTimer {
    fn new() -> impl PinInit<Self, kernel::error::Error> {
        try_pin_init!(Self {
            timer <- HrTimer::new(),
            shared: Arc::pin_init(Shared::new(), flags::GFP_KERNEL)?,
        })
    }
}

impl HrTimerCallback for BoxIntrusiveHrTimer {
    type Pointer<'a> = Pin<KBox<Self>>;

    fn run(this: Pin<&mut Self>, _ctx: HrTimerCallbackContext<'_, Self>) -> HrTimerRestart {
        pr_info!("Timer called\n");

        let flag = this.shared.flag.fetch_add(1, ordering::Full);
        this.shared.cond.complete_all();

        if flag == 4 {
            HrTimerRestart::NoRestart
        } else {
            HrTimerRestart::Restart
        }
    }
}

impl_has_hr_timer! {
    impl HasHrTimer<Self> for BoxIntrusiveHrTimer {
        mode: RelativeMode<Monotonic>, field: self.timer
    }
}

let has_timer = Box::pin_init(BoxIntrusiveHrTimer::new(), GFP_KERNEL)?;
let shared = has_timer.shared.clone();
let _handle = has_timer.start(Delta::from_micros(200));

while shared.flag.load(ordering::Relaxed) != 5 {
    shared.cond.wait_for_completion();
}

pr_info!("Counted to 5\n");

§Using an intrusive timer in an Arc


#[pin_data]
struct ArcIntrusiveHrTimer {
    #[pin]
    timer: HrTimer<Self>,
    #[pin]
    flag: Atomic<u64>,
    #[pin]
    cond: Completion,
}

impl ArcIntrusiveHrTimer {
    fn new() -> impl PinInit<Self> {
        pin_init!(Self {
            timer <- HrTimer::new(),
            flag <- Atomic::new(0),
            cond <- Completion::new(),
        })
    }
}

impl HrTimerCallback for ArcIntrusiveHrTimer {
    type Pointer<'a> = Arc<Self>;

    fn run(
        this: ArcBorrow<'_, Self>,
        _ctx: HrTimerCallbackContext<'_, Self>,
    ) -> HrTimerRestart {
        pr_info!("Timer called\n");

        let flag = this.flag.fetch_add(1, ordering::Full);
        this.cond.complete_all();

        if flag == 4 {
            HrTimerRestart::NoRestart
        } else {
            HrTimerRestart::Restart
        }
    }
}

impl_has_hr_timer! {
    impl HasHrTimer<Self> for ArcIntrusiveHrTimer {
        mode: RelativeMode<Monotonic>, field: self.timer
    }
}

let has_timer = Arc::pin_init(ArcIntrusiveHrTimer::new(), GFP_KERNEL)?;
let _handle = has_timer.clone().start(Delta::from_micros(200));

while has_timer.flag.load(ordering::Relaxed) != 5 {
    has_timer.cond.wait_for_completion();
}

pr_info!("Counted to 5\n");

§Using a stack-based timer


#[pin_data]
struct IntrusiveHrTimer {
    #[pin]
    timer: HrTimer<Self>,
    #[pin]
    flag: Atomic<u64>,
    #[pin]
    cond: Completion,
}

impl IntrusiveHrTimer {
    fn new() -> impl PinInit<Self> {
        pin_init!(Self {
            timer <- HrTimer::new(),
            flag <- Atomic::new(0),
            cond <- Completion::new(),
        })
    }
}

impl HrTimerCallback for IntrusiveHrTimer {
    type Pointer<'a> = Pin<&'a Self>;

    fn run(this: Pin<&Self>, _ctx: HrTimerCallbackContext<'_, Self>) -> HrTimerRestart {
        pr_info!("Timer called\n");

        this.flag.store(1, ordering::Release);
        this.cond.complete_all();

        HrTimerRestart::NoRestart
    }
}

impl_has_hr_timer! {
    impl HasHrTimer<Self> for IntrusiveHrTimer {
        mode: RelativeMode<Monotonic>, field: self.timer
    }
}

stack_pin_init!( let has_timer = IntrusiveHrTimer::new() );
has_timer.as_ref().start_scoped(Delta::from_micros(200), || {
    while has_timer.flag.load(ordering::Relaxed) != 1 {
        has_timer.cond.wait_for_completion();
    }
});

pr_info!("Flag raised\n");

§Using a mutable stack-based timer


#[pin_data]
struct Shared {
    #[pin]
    flag: Atomic<u64>,
    #[pin]
    cond: Completion,
}

impl Shared {
    fn new() -> impl PinInit<Self> {
        pin_init!(Self {
            flag <- Atomic::new(0),
            cond <- Completion::new(),
        })
    }
}

#[pin_data]
struct IntrusiveHrTimer {
    #[pin]
    timer: HrTimer<Self>,
    shared: Arc<Shared>,
}

impl IntrusiveHrTimer {
    fn new() -> impl PinInit<Self, kernel::error::Error> {
        try_pin_init!(Self {
            timer <- HrTimer::new(),
            shared: Arc::pin_init(Shared::new(), flags::GFP_KERNEL)?,
        })
    }
}

impl HrTimerCallback for IntrusiveHrTimer {
    type Pointer<'a> = Pin<&'a mut Self>;

    fn run(this: Pin<&mut Self>, _ctx: HrTimerCallbackContext<'_, Self>) -> HrTimerRestart {
        pr_info!("Timer called\n");

        let flag = this.shared.flag.fetch_add(1, ordering::Full);
        this.shared.cond.complete_all();

        if flag == 4 {
            HrTimerRestart::NoRestart
        } else {
            HrTimerRestart::Restart
        }
    }
}

impl_has_hr_timer! {
    impl HasHrTimer<Self> for IntrusiveHrTimer {
        mode: RelativeMode<Monotonic>, field: self.timer
    }
}

stack_try_pin_init!( let has_timer =? IntrusiveHrTimer::new() );
let shared = has_timer.shared.clone();

has_timer.as_mut().start_scoped(Delta::from_micros(200), || {
    while shared.flag.load(ordering::Relaxed) != 5 {
        shared.cond.wait_for_completion();
    }
});

pr_info!("Counted to 5\n");

Structs§

Enums§

Traits§

Type Aliases§