Skip to main content

osal_rs/traits/
timer.rs

1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20
21//! Software timer trait for delayed and periodic callbacks.
22//!
23//! Timers execute callback functions in the context of a timer service task,
24//! enabling delayed operations and periodic tasks without dedicated threads.
25//!
26//! # Overview
27//!
28//! Software timers provide a way to execute callback functions at specified
29//! intervals without creating dedicated tasks. All timer callbacks run in
30//! the context of a single timer service daemon task.
31//!
32//! # Timer Types
33//!
34//! - **One-shot**: Expires once after the period elapses
35//! - **Auto-reload (Periodic)**: Automatically restarts after expiring
36//!
37//! # Timer Service Task
38//!
39//! All timer callbacks execute in a dedicated timer service task that:
40//! - Has a configurable priority
41//! - Processes timer commands from a queue
42//! - Executes callbacks sequentially (not in parallel)
43//!
44//! # Important Constraints
45//!
46//! - Timer callbacks should be short and non-blocking
47//! - Callbacks should not call blocking RTOS APIs (may cause deadlock)
48//! - Long callbacks delay other timer expirations
49//! - Use task notifications or queues to defer work to other tasks
50//!
51//! # Accuracy
52//!
53//! Timer accuracy depends on:
54//! - System tick rate (e.g., 1ms for 1000 Hz)
55//! - Timer service task priority
56//! - Duration of other timer callbacks
57//! - System load
58//!
59//! # Examples
60//!
61//! ```ignore
62//! use osal_rs::os::Timer;
63//! use core::time::Duration;
64//!
65//! // One-shot timer
66//! let once = Timer::new(
67//!     "timeout",
68//!     Duration::from_secs(5),
69//!     false,  // Not auto-reload
70//!     None,
71//!     |_timer, _param| {
72//!         println!("Timeout!");
73//!         Ok(None)
74//!     }
75//! ).unwrap();
76//! once.start(0);
77//!
78//! // Periodic timer
79//! let periodic = Timer::new(
80//!     "heartbeat",
81//!     Duration::from_millis(500),
82//!     true,  // Auto-reload
83//!     None,
84//!     |_timer, _param| {
85//!         toggle_led();
86//!         Ok(None)
87//!     }
88//! ).unwrap();
89//! periodic.start(0);
90//! ```
91
92use core::any::Any;
93
94use alloc::{boxed::Box, sync::Arc};
95
96use crate::os::types::TickType;
97use crate::utils::{OsalRsBool, Result};
98
99/// Type-erased parameter for timer callbacks.
100///
101/// Allows passing arbitrary data to timer callback functions in a type-safe
102/// manner. The parameter is wrapped in an `Arc` for safe sharing and can be
103/// downcast to its original type.
104///
105/// # Thread Safety
106///
107/// The inner type must implement `Any + Send + Sync` since timer callbacks
108/// execute in the timer service task context.
109///
110/// # Examples
111///
112/// ```ignore
113/// use std::sync::Arc;
114/// use osal_rs::traits::TimerParam;
115///
116/// // Create a parameter
117/// let count: TimerParam = Arc::new(0u32);
118///
119/// // In timer callback, downcast to access
120/// if let Some(value) = param.downcast_ref::<u32>() {
121///     println!("Count: {}", value);
122/// }
123/// ```
124pub type TimerParam = Arc<dyn Any + Send + Sync>;
125
126/// Timer callback function pointer type.
127///
128/// Callbacks receive the timer handle and optional parameter,
129/// and can return an updated parameter value.
130///
131/// # Parameters
132///
133/// - `Box<dyn Timer>` - Handle to the timer that expired
134/// - `Option<TimerParam>` - Optional parameter passed at creation
135///
136/// # Returns
137///
138/// `Result<TimerParam>` - Updated parameter or error
139///
140/// # Execution Context
141///
142/// Callbacks execute in the timer service task, not ISR context.
143/// They should be short and avoid blocking operations.
144///
145/// # Trait Bounds
146///
147/// The function must be `Send + Sync + 'static` to safely execute
148/// in the timer service task.
149///
150/// # Examples
151///
152/// ```ignore
153/// use osal_rs::traits::{Timer, TimerParam};
154/// use std::sync::Arc;
155///
156/// let callback: Box<TimerFnPtr> = Box::new(|timer, param| {
157///     if let Some(p) = param {
158///         if let Some(count) = p.downcast_ref::<u32>() {
159///             println!("Timer expired, count: {}", count);
160///             return Ok(Arc::new(*count + 1));
161///         }
162///     }
163///     Ok(Arc::new(0u32))
164/// });
165/// ```
166pub type TimerFnPtr = dyn Fn(Box<dyn Timer>, Option<TimerParam>) -> Result<TimerParam> + Send + Sync + 'static;
167
168/// Software timer for delayed and periodic callbacks.
169///
170/// Timers run callbacks in the timer service task context, not ISR context.
171/// They can be one-shot or auto-reloading (periodic).
172///
173/// # Timer Lifecycle
174///
175/// 1. **Creation**: `Timer::new()` with name, period, auto-reload flag, and callback
176/// 2. **Start**: `start()` begins the timer countdown
177/// 3. **Expiration**: Callback executes when period elapses
178/// 4. **Auto-reload**: If enabled, timer automatically restarts
179/// 5. **Management**: Use `stop()`, `reset()`, `change_period()` to control
180/// 6. **Cleanup**: `delete()` frees resources
181///
182/// # Command Queue
183///
184/// Timer operations (start, stop, etc.) send commands to a queue processed
185/// by the timer service task. The `ticks_to_wait` parameter controls how
186/// long to wait if the queue is full.
187///
188/// # Callback Constraints
189///
190/// - Keep callbacks short (< 1ms ideally)
191/// - Avoid blocking operations (delays, mutex waits, etc.)
192/// - Don't call APIs that might block indefinitely
193/// - Use task notifications or queues to defer work to tasks
194///
195/// # Examples
196///
197/// ## One-shot Timer
198///
199/// ```ignore
200/// use osal_rs::os::Timer;
201/// use core::time::Duration;
202/// 
203/// let timer = Timer::new(
204///     "alarm",
205///     Duration::from_secs(5),
206///     false,  // One-shot
207///     None,
208///     |_timer, _param| {
209///         println!("Alarm!");
210///         trigger_alarm();
211///         Ok(None)
212///     }
213/// ).unwrap();
214/// 
215/// timer.start(0);
216/// // Expires once after 5 seconds
217/// ```
218///
219/// ## Periodic Timer
220///
221/// ```ignore
222/// use std::sync::Arc;
223///
224/// let counter = Arc::new(0u32);
225/// let periodic = Timer::new(
226///     "counter",
227///     Duration::from_millis(100),
228///     true,  // Auto-reload
229///     Some(counter.clone()),
230///     |_timer, param| {
231///         if let Some(p) = param {
232///             if let Some(count) = p.downcast_ref::<u32>() {
233///                 println!("Count: {}", count);
234///                 return Ok(Arc::new(*count + 1));
235///             }
236///         }
237///         Ok(Arc::new(0u32))
238///     }
239/// ).unwrap();
240/// 
241/// periodic.start(0);
242/// // Runs every 100ms until stopped
243/// ```
244pub trait Timer {
245    /// Starts or restarts the timer.
246    ///
247    /// If the timer is already running, this command resets it to its full
248    /// period (equivalent to calling `reset()`). If stopped, the timer begins
249    /// counting down from its period.
250    ///
251    /// # Parameters
252    ///
253    /// * `ticks_to_wait` - Maximum ticks to wait if command queue is full:
254    ///   - `0`: Return immediately if queue full
255    ///   - `n`: Wait up to n ticks
256    ///   - `TickType::MAX`: Wait forever
257    ///
258    /// # Returns
259    ///
260    /// * `True` - Command sent successfully to timer service
261    /// * `False` - Failed to send command (queue full, timeout)
262    ///
263    /// # Timing
264    ///
265    /// The timer begins counting after the command is processed by the
266    /// timer service task, not immediately when this function returns.
267    ///
268    /// # Examples
269    ///
270    /// ```ignore
271    /// use osal_rs::os::Timer;
272    ///
273    /// // Start immediately, don't wait
274    /// if timer.start(0).into() {
275    ///     println!("Timer started");
276    /// }
277    ///
278    /// // Wait up to 100 ticks for command queue
279    /// timer.start(100);
280    /// ```
281    fn start(&self, ticks_to_wait: TickType) -> OsalRsBool;
282    
283    /// Stops the timer.
284    ///
285    /// The timer will not expire until started again with `start()` or `reset()`.
286    /// For periodic timers, this stops the automatic reloading.
287    ///
288    /// # Parameters
289    ///
290    /// * `ticks_to_wait` - Maximum ticks to wait if command queue is full:
291    ///   - `0`: Return immediately if queue full
292    ///   - `n`: Wait up to n ticks
293    ///   - `TickType::MAX`: Wait forever
294    ///
295    /// # Returns
296    ///
297    /// * `True` - Command sent successfully to timer service
298    /// * `False` - Failed to send command (queue full, timeout)
299    ///
300    /// # State
301    ///
302    /// If the timer is already stopped, this command has no effect but
303    /// still returns `True`.
304    ///
305    /// # Examples
306    ///
307    /// ```ignore
308    /// use osal_rs::os::Timer;
309    ///
310    /// // Stop the timer, wait up to 100 ticks
311    /// if timer.stop(100).into() {
312    ///     println!("Timer stopped");
313    /// }
314    ///
315    /// // Later, restart it
316    /// timer.start(100);
317    /// ```
318    fn stop(&self, ticks_to_wait: TickType)  -> OsalRsBool;
319    
320    /// Resets the timer to its full period.
321    ///
322    /// If the timer is running, this restarts it from the beginning of its
323    /// period. If the timer is stopped, this starts it. This is useful for
324    /// implementing watchdog-style timers that must be periodically reset.
325    ///
326    /// # Parameters
327    ///
328    /// * `ticks_to_wait` - Maximum ticks to wait if command queue is full:
329    ///   - `0`: Return immediately if queue full
330    ///   - `n`: Wait up to n ticks
331    ///   - `TickType::MAX`: Wait forever
332    ///
333    /// # Returns
334    ///
335    /// * `True` - Command sent successfully to timer service
336    /// * `False` - Failed to send command (queue full, timeout)
337    ///
338    /// # Use Cases
339    ///
340    /// - Watchdog timer: Reset timer to prevent timeout
341    /// - Activity timer: Reset when activity detected
342    /// - Timeout extension: Give more time before expiration
343    ///
344    /// # Examples
345    ///
346    /// ```ignore
347    /// use osal_rs::os::Timer;
348    /// use core::time::Duration;
349    ///
350    /// // Watchdog timer pattern
351    /// let watchdog = Timer::new(
352    ///     "watchdog",
353    ///     Duration::from_secs(10),
354    ///     false,
355    ///     None,
356    ///     |_timer, _param| {
357    ///         println!("WATCHDOG TIMEOUT!");
358    ///         system_reset();
359    ///         Ok(None)
360    ///     }
361    /// ).unwrap();
362    ///
363    /// watchdog.start(0);
364    ///
365    /// // In main loop: reset watchdog to prevent timeout
366    /// loop {
367    ///     do_work();
368    ///     watchdog.reset(0);  // "Feed" the watchdog
369    /// }
370    /// ```
371    fn reset(&self, ticks_to_wait: TickType) -> OsalRsBool;
372    
373    /// Changes the timer period.
374    ///
375    /// Updates the timer period. The new period takes effect immediately:
376    /// - If the timer is running, it continues with the new period
377    /// - The remaining time is adjusted proportionally
378    /// - For periodic timers, future expirations use the new period
379    ///
380    /// # Parameters
381    ///
382    /// * `new_period_in_ticks` - New timer period in ticks
383    /// * `ticks_to_wait` - Maximum ticks to wait if command queue is full:
384    ///   - `0`: Return immediately if queue full
385    ///   - `n`: Wait up to n ticks
386    ///   - `TickType::MAX`: Wait forever
387    ///
388    /// # Returns
389    ///
390    /// * `True` - Command sent successfully to timer service
391    /// * `False` - Failed to send command (queue full, timeout)
392    ///
393    /// # Behavior
394    ///
395    /// - If timer has already expired and is auto-reload, the new period
396    ///   applies to the next expiration
397    /// - If timer is stopped, the new period will be used when started
398    ///
399    /// # Examples
400    ///
401    /// ```ignore
402    /// use osal_rs::os::Timer;
403    /// use core::time::Duration;
404    ///
405    /// let timer = Timer::new(
406    ///     "adaptive",
407    ///     Duration::from_millis(100),
408    ///     true,
409    ///     None,
410    ///     |_timer, _param| Ok(None)
411    /// ).unwrap();
412    ///
413    /// timer.start(0);
414    ///
415    /// // Later, adjust the period based on system load
416    /// if system_busy() {
417    ///     // Slow down to 500ms
418    ///     timer.change_period(500, 100);
419    /// } else {
420    ///     // Speed up to 100ms
421    ///     timer.change_period(100, 100);
422    /// }
423    /// ```
424    fn change_period(&self, new_period_in_ticks: TickType, ticks_to_wait: TickType) -> OsalRsBool;
425    
426    /// Deletes the timer and frees its resources.
427    ///
428    /// Terminates the timer and releases its resources. After deletion,
429    /// the timer handle becomes invalid and should not be used.
430    ///
431    /// # Parameters
432    ///
433    /// * `ticks_to_wait` - Maximum ticks to wait if command queue is full:
434    ///   - `0`: Return immediately if queue full
435    ///   - `n`: Wait up to n ticks
436    ///   - `TickType::MAX`: Wait forever
437    ///
438    /// # Returns
439    ///
440    /// * `True` - Command sent successfully to timer service
441    /// * `False` - Failed to send command (queue full, timeout)
442    ///
443    /// # Safety
444    ///
445    /// - The timer should be stopped before deletion (recommended)
446    /// - Do not use the timer handle after calling this
447    /// - The timer is deleted asynchronously by the timer service task
448    ///
449    /// # Best Practice
450    ///
451    /// Stop the timer before deleting it to ensure clean shutdown:
452    ///
453    /// ```ignore
454    /// timer.stop(100);
455    /// timer.delete(100);
456    /// ```
457    ///
458    /// # Examples
459    ///
460    /// ```ignore
461    /// use osal_rs::os::Timer;
462    /// use core::time::Duration;
463    ///
464    /// let mut timer = Timer::new(
465    ///     "temporary",
466    ///     Duration::from_secs(1),
467    ///     false,
468    ///     None,
469    ///     |_timer, _param| Ok(None)
470    /// ).unwrap();
471    ///
472    /// timer.start(0);
473    /// // ... use timer ...
474    ///
475    /// // Clean shutdown
476    /// timer.stop(100);
477    /// if timer.delete(100).into() {
478    ///     println!("Timer deleted");
479    /// }
480    /// ```
481    fn delete(&mut self, ticks_to_wait: TickType) -> OsalRsBool;
482}