Skip to content
Open
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
8 changes: 8 additions & 0 deletions include/FreeRTOS.h
Original file line number Diff line number Diff line change
Expand Up @@ -1770,6 +1770,14 @@
#define traceRETURN_vTaskDelete()
#endif

#ifndef traceENTER_xTaskPeriodicDelay
#define traceENTER_xTaskPeriodicDelay( pxPreviousWakeTime, xTimeIncrement )
#endif

#ifndef traceRETURN_xTaskPeriodicDelay
#define traceRETURN_xTaskPeriodicDelay( xIncrements )
#endif

#ifndef traceENTER_xTaskDelayUntil
#define traceENTER_xTaskDelayUntil( pxPreviousWakeTime, xTimeIncrement )
#endif
Expand Down
37 changes: 37 additions & 0 deletions include/task.h
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,43 @@ void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION;
*/
void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION;

/**
* task. h
* @code{c}
* TickType_t xTaskPeriodicDelay( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );
* @endcode
*
* INCLUDE_xTaskDelayUntil must be defined as 1 for this function to be available.
* See the configuration section for more information.
*
* Periodic task delay to ensure a constant execution frequency.
*
* This function is similar to xTaskDelayUntil () with a few important differences:
* - pxPreviousWakeTime contains the last past wake time, so it never runs away
* - if you suspend the task, when you resume it pxPreviousWakeTime will instantly
* catch up all skipped increments
* - it returns the number of increments added to pxPreviosWakeTime
*
* @param pxPreviousWakeTime Pointer to a variable that holds the time at which the
* task was last unblocked. The variable must be initialised with the current time
* prior to its first use. Following this the variable is automatically updated.
*
* @param xTimeIncrement The cycle time period. The task will be unblocked at
* time *pxPreviousWakeTime + xTimeIncrement. Passing the same xTimeIncrement
* parameter value will cause the task to execute with a fixed interval.
*
* @return Number of times xTimeIncrement has been added to pxPreviousWakeTime.
* It is 0 on the first call or if not enough ticks have been elapsed since the
* last call, 1 in normal circumstances or more than 1 if some period has been
* skipped for some reason (e.g. when the caller task is suspended for more than
* xTimeIncrement ticks).
*
* \defgroup xTaskPeriodicDelay xTaskPeriodicDelay
* \ingroup TaskCtrl
*/
TickType_t xTaskPeriodicDelay( TickType_t * const pxPreviousWakeTime,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
TickType_t xTaskPeriodicDelay( TickType_t * const pxPreviousWakeTime,
UBaseType_t xTaskPeriodicDelay( TickType_t * const pxPreviousWakeTime,

Let's use the unsigned based type since this is a simple count

Copy link
Copy Markdown
Author

@ntd ntd May 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My problem is this code:

/* This plays well with overflow */
const TickType_t xTicksElapsed = xTickCount - *pxPreviousWakeTime;

needs everything to be of the same type to work properly, so xTicksElapsed must be TickType_t. Then xIncrements (that is, what xTaskPeriodicDelay returns) is calculated as:

xIncrements = xTicksElapsed / xTimeIncrement;

and if I declare it as UBaseType_t I get the following error on my host:

error: conversion from ‘TickType_t’ {aka ‘long unsigned int’} to ‘UBaseType_t’ {aka ‘unsigned char’} may change value [[-Werror=conversion](https://gcc.gnu.org/onlinedocs/gcc-16.1.0/gcc/Warning-Options.html#index-Wconversion)]

I can cast it to remove the error, but in that case I think the result can easily overflow if I suspend a task for too much time.

const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION;

/**
* task. h
* @code{c}
Expand Down
56 changes: 56 additions & 0 deletions tasks.c
Original file line number Diff line number Diff line change
Expand Up @@ -2374,6 +2374,62 @@

#if ( INCLUDE_xTaskDelayUntil == 1 )

TickType_t xTaskPeriodicDelay( TickType_t * const pxPreviousWakeTime,
const TickType_t xTimeIncrement )
{
TickType_t xIncrements, xTicksIncrements, xTicksToWait;

Check warning on line 2380 in tasks.c

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define each identifier in a dedicated statement.

See more on https://sonarcloud.io/project/issues?id=FreeRTOS_FreeRTOS-Kernel&issues=AZ52UlDTGzJsVDTR69dN&open=AZ52UlDTGzJsVDTR69dN&pullRequest=1368

traceENTER_xTaskPeriodicDelay( pxPreviousWakeTime, xTimeIncrement );

configASSERT( pxPreviousWakeTime );
configASSERT( ( xTimeIncrement > 0U ) );

vTaskSuspendAll();
{
/* As long as everything is the same type, this plays well with overflows */
const TickType_t xTicksElapsed = xTickCount - *pxPreviousWakeTime;

configASSERT( uxSchedulerSuspended == 1U );

/* Number of increments to catch up: it could be 0 if
* not enough ticks have elapsed, 1 in the common case or
* more than 1 if the task has not been resumed in time */
xIncrements = xTicksElapsed / xTimeIncrement;
xTicksIncrements = xIncrements * xTimeIncrement;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably need a check for overflow here. Don't want xIncrements * xTimeIncrement to be exactly one more than the xTicksIncrements maximum possible value.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xTicksIncrements cannot overflow. If you substitute xIncrements you get:

xTicksIncrements = xTicksElapsed / xTimeIncrement * xTimeIncrement;

so xTicksIncrements would never exceed xTicksElapsed.


/* Update to the last wake time */
*pxPreviousWakeTime += xTicksIncrements;

/* Ticks to the next wake time */
xTicksToWait = xTimeIncrement - ( xTicksElapsed - xTicksIncrements );

if( xTicksToWait > 0 )
{
prvAddCurrentTaskToDelayedList( xTicksToWait, pdFALSE );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}

/* Force a reschedule if xTaskResumeAll has not already done so, we may
* have put ourselves to sleep. */
if( xTaskResumeAll() == pdFALSE )
{
taskYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}

traceRETURN_xTaskPeriodicDelay( xIncrements );

return xIncrements;
}


BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
const TickType_t xTimeIncrement )
{
Expand Down