Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 17 additions & 4 deletions Documentation/reference/os/time_clock.rst
Original file line number Diff line number Diff line change
Expand Up @@ -675,22 +675,35 @@ The callback runs in the timer interrupt context, so only limited NuttX interfac
are available, such as ``mq_send()``, ``sigqueue()``, ``nxevent_post()``, or ``kill()``,
to communicate with tasks.

High-resolution timers (hrtimers) use a spinlock to protect internal timer data structures,
allowing them to be safely accessed from interrupt context. This design makes hrtimers
suitable for high-frequency timer operations.

However, in SMP systems, a potential issue arises when a user cancels a timer that is currently
executing: the timer instance may be freed prematurely while its callback is still running.
To address this problem, a state machine is introduced in the hrtimer implementation:

.. image:: hrtimer_state_machine.png
:alt: hrtimer state machine

Users may call ``hrtimer_cancel_sync()`` to synchronously cancel a timer and wait until the
timer becomes fully inactive. In contrast, ``hrtimer_cancel()`` is a non-blocking variant
that returns immediately without waiting for the timer to stop executing.

- :c:func:`hrtimer_init`
- :c:func:`hrtimer_cancel`
- :c:func:`hrtimer_cancel_sync`
- :c:func:`hrtimer_start`
- High-resolution Timer Callback

.. c:function:: void hrtimer_init(FAR hrtimer_t *hrtimer, hrtentry_t func, \
FAR void *arg)
.. c:function:: void hrtimer_init(FAR hrtimer_t *hrtimer, hrtentry_t func)

This function initializes a high-resolution timer instance.
Sets the expiration callback and its argument. The timer is
not started by this function.

:param hrtimer: Pointer to hrtimer instance
:param func: Expiration callback function
:param arg: Callback argument

:return: None.

Expand Down Expand Up @@ -745,7 +758,7 @@ to communicate with tasks.

**POSIX Compatibility:** This is a NON-POSIX interface.

.. c:type:: void (*hrtentry_t)(FAR struct hrtimer_s *)
.. c:type:: uint64_t (*hrtimer_cb)(FAR hrtimer_t *hrtimer, uint64_t expired)

**High-resolution Timer Callback**: when a hrtimer expires,
the callback function with this type is called.
38 changes: 10 additions & 28 deletions include/nuttx/hrtimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,6 @@ enum hrtimer_mode_e
HRTIMER_MODE_REL /* Relative delay from now */
};

/* High-resolution timer states
*
* State transitions are managed internally by the hrtimer framework.
* Callers must not modify the state directly.
*/

enum hrtimer_state_e
{
HRTIMER_STATE_INACTIVE = 0, /* Timer is inactive and not queued */
HRTIMER_STATE_ARMED, /* Timer is armed and waiting for expiry */
HRTIMER_STATE_RUNNING, /* Timer callback is currently executing */
HRTIMER_STATE_CANCELED /* Timer canceled (callback may be running) */
};

/* Forward declarations */

struct hrtimer_s;
Expand All @@ -79,7 +65,8 @@ typedef struct hrtimer_node_s hrtimer_node_t;
* timer context and must not block.
*/

typedef uint32_t (*hrtimer_cb)(FAR struct hrtimer_s *hrtimer);
typedef CODE uint64_t
(*hrtimer_cb)(FAR hrtimer_t *hrtimer, uint64_t expired);

/* Red-black tree node used to order hrtimers by expiration time */

Expand All @@ -97,11 +84,9 @@ struct hrtimer_node_s

struct hrtimer_s
{
hrtimer_node_t node; /* RB-tree node for sorted insertion */
enum hrtimer_state_e state; /* Current timer state */
hrtimer_cb func; /* Expiration callback function */
FAR void *arg; /* Argument passed to callback */
uint64_t expired; /* Absolute expiration time (ns) */
hrtimer_node_t node; /* RB-tree node for sorted insertion */
hrtimer_cb func; /* Expiration callback function */
uint64_t expired; /* Absolute expiration time (ns) */
};

/****************************************************************************
Expand Down Expand Up @@ -134,13 +119,10 @@ extern "C"
****************************************************************************/

static inline_function
void hrtimer_init(FAR hrtimer_t *hrtimer,
hrtimer_cb func,
FAR void *arg)
void hrtimer_init(FAR hrtimer_t *hrtimer, hrtimer_cb func)
{
hrtimer->state = HRTIMER_STATE_INACTIVE;
hrtimer->func = func;
hrtimer->arg = arg;
memset(hrtimer, 0, sizeof(hrtimer_t));
hrtimer->func = func;
}

/****************************************************************************
Expand Down Expand Up @@ -202,15 +184,15 @@ int hrtimer_cancel_sync(FAR hrtimer_t *hrtimer);
*
* Input Parameters:
* hrtimer - Timer instance to start
* ns - Expiration time in nanoseconds
* expired - Expiration time in nanoseconds
* mode - HRTIMER_MODE_ABS or HRTIMER_MODE_REL
*
* Returned Value:
* OK on success; a negated errno value on failure.
****************************************************************************/

int hrtimer_start(FAR hrtimer_t *hrtimer,
uint64_t ns,
uint64_t expired,
enum hrtimer_mode_e mode);

#undef EXTERN
Expand Down
117 changes: 107 additions & 10 deletions sched/hrtimer/hrtimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@
#include <nuttx/clock.h>
#include <nuttx/hrtimer.h>

#ifdef CONFIG_HRTIMER

/****************************************************************************
* Public Types
****************************************************************************/

/* Red-black tree head for managing active high-resolution timers.
*
* Timers are ordered by expiration time, the earliest expiring timer
* being the left-most (minimum) node in the tree.
*/

RB_HEAD(hrtimer_tree_s, hrtimer_node_s);

/****************************************************************************
* Public Data
****************************************************************************/
Expand All @@ -44,13 +58,11 @@ extern spinlock_t g_hrtimer_spinlock;

extern struct hrtimer_tree_s g_hrtimer_tree;

/****************************************************************************
* Public Types
****************************************************************************/

/* Red-black tree head for managing active hrtimers */
/* Array of pointers to currently running high-resolution timers
* for each CPU in SMP configurations. Index corresponds to CPU ID.
*/

RB_HEAD(hrtimer_tree_s, hrtimer_node_s);
extern FAR hrtimer_t *g_hrtimer_running[CONFIG_SMP_NCPUS];

/****************************************************************************
* Public Function Prototypes
Expand Down Expand Up @@ -147,14 +159,13 @@ int hrtimer_starttimer(uint64_t ns)
* b - Pointer to the second hrtimer node.
*
* Returned Value:
* >0 if b expires before a
* 0 if a and b expire at the same time
* <0 if b expires after a
* <0 if a expires before b
* >=0 if a expires after b
****************************************************************************/

static inline_function
int hrtimer_compare(FAR const hrtimer_node_t *a,
FAR const hrtimer_node_t *b)
FAR const hrtimer_node_t *b)
{
FAR const hrtimer_t *atimer = (FAR const hrtimer_t *)a;
FAR const hrtimer_t *btimer = (FAR const hrtimer_t *)b;
Expand All @@ -173,4 +184,90 @@ int hrtimer_compare(FAR const hrtimer_node_t *a,

RB_PROTOTYPE(hrtimer_tree_s, hrtimer_node_s, entry, hrtimer_compare);

/****************************************************************************
* Name: hrtimer_is_armed
*
* Description:
* Test whether a timer is currently armed (inserted into the RB-tree).
*
* Returned Value:
* true if armed, false otherwise.
****************************************************************************/

static inline_function bool hrtimer_is_armed(FAR hrtimer_t *hrtimer)
{
/* RB-tree root has NULL parent, so root must be checked explicitly */

return RB_PARENT(&hrtimer->node, entry) != NULL ||
RB_ROOT(&g_hrtimer_tree) == &hrtimer->node;
}

/****************************************************************************
* Name: hrtimer_remove
*
* Description:
* Remove a timer from the RB-tree and mark it as unarmed.
****************************************************************************/

static inline_function void hrtimer_remove(FAR hrtimer_t *hrtimer)
{
RB_REMOVE(hrtimer_tree_s, &g_hrtimer_tree, &hrtimer->node);

/* Explicitly clear parent to mark the timer as unarmed */

RB_PARENT(&hrtimer->node, entry) = NULL;
}

/****************************************************************************
* Name: hrtimer_insert
*
* Description:
* Insert a timer into the RB-tree according to its expiration time.
****************************************************************************/

static inline_function void hrtimer_insert(FAR hrtimer_t *hrtimer)
{
RB_INSERT(hrtimer_tree_s, &g_hrtimer_tree, &hrtimer->node);
}

/****************************************************************************
* Name: hrtimer_get_first
*
* Description:
* Return the earliest expiring armed timer.
*
* Returned Value:
* Pointer to the earliest timer, or NULL if none are armed.
****************************************************************************/

static inline_function FAR hrtimer_t *hrtimer_get_first(void)
{
return (FAR hrtimer_t *)RB_MIN(hrtimer_tree_s, &g_hrtimer_tree);
}

/****************************************************************************
* Name: hrtimer_is_first
*
* Description:
* Test whether the given high-resolution timer is the earliest
* expiring timer in the RB-tree.
*
* In a red-black tree ordered by expiration time, the earliest timer
* is represented by the left-most node. Therefore, a timer is the
* earliest one if it has no left child.
*
* Input Parameters:
* hrtimer - Pointer to the high-resolution timer to be tested.
*
* Returned Value:
* true - The timer is the earliest expiring armed timer.
* false - The timer is not the earliest timer.
****************************************************************************/

static inline_function bool hrtimer_is_first(FAR hrtimer_t *hrtimer)
{
return RB_LEFT(&hrtimer->node, entry) == NULL;
}

#endif /* CONFIG_HRTIMER */
#endif /* __SCHED_HRTIMER_HRTIMER_H */
Loading
Loading