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