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}