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