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}