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}