vTaskSetTimeOutState(...) and xTaskCheckForTimeOut(...) from interrupt

parmi wrote on Tuesday, July 02, 2019:

Can the vTaskSetTimeOutState(…) and xTaskCheckForTimeOut(…) APIs be called by an interrupt service routine?

richard_damon wrote on Tuesday, July 02, 2019:

I don’t think so, they would need critical sections, which need to be done differently in ISRs. In general, if it doesn’t say FromISR, it isn’t usable from an ISR.

parmi wrote on Tuesday, July 02, 2019:

I figured it was like that, is there a way to make these APIs safe?

rtel wrote on Tuesday, July 02, 2019:

You need to change the critical section macros, as per the example for
vTaskSetTimeOutState() below.

void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut )
{
UBaseType_t uxSavedInterruptStatus;

	configASSERT( pxTimeOut );
	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		pxTimeOut->xOverflowCount = xNumOfOverflows;
		pxTimeOut->xTimeOnEntering = xTickCount;
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
}

parmi wrote on Monday, July 08, 2019:

ok, thanks a lot for the solution.
Is there a way to add this API to FreeRTOS without having to modify the “tasks.c” file? for example by creating a “tasks_helper.c” file.
Because if I create an additional *.c I can’t access the “xNumOfOverflows” and “xTickCount” variables

rtel wrote on Monday, July 08, 2019:

You can add your code into a header file called
freertos_task_c_additions.h, then set
configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H to 1 in FreeRTOSConfig.h to
have the header file included at the bottom of the source file so you
have access to the private data.

https://sourceforge.net/p/freertos/code/HEAD/tree/tags/V10.2.1/FreeRTOS/Source/tasks.c#l5203

parmi wrote on Monday, July 08, 2019:

I have created “freertos_tasks_c_additions.h” file:

#include "FreeRTOS.h"
#include "task.h"

void vTaskSetTimeOutStateFromISR(TimeOut_t * const pxTimeOut);

BaseType_t xTaskCheckForTimeOutFromISR(TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait);

tasks_custom.cpp

#include "freertos_tasks_c_additions.h"

void vTaskSetTimeOutStateFromISR(TimeOut_t * const pxTimeOut)
{
	UBaseType_t uxSavedInterruptStatus;
	
	configASSERT(pxTimeOut);
	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		pxTimeOut->xOverflowCount = xNumOfOverflows;
		pxTimeOut->xTimeOnEntering = xTickCount;
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
}

BaseType_t xTaskCheckForTimeOutFromISR(TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait)
{
	BaseType_t xReturn;
	UBaseType_t uxSavedInterruptStatus;

	configASSERT(pxTimeOut);
	configASSERT(pxTicksToWait);

	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		/* Minor optimisation.  The tick count cannot change in this block. */
		const TickType_t xConstTickCount = xTickCount;
		const TickType_t xElapsedTime = xConstTickCount - pxTimeOut->xTimeOnEntering;

#if( INCLUDE_xTaskAbortDelay == 1 )
		if (pxCurrentTCB->ucDelayAborted != pdFALSE)
		{
			/* The delay was aborted, which is not the same as a time out,
			but has the same result. */
			pxCurrentTCB->ucDelayAborted = pdFALSE;
			xReturn = pdTRUE;
		}
		else
#endif

#if ( INCLUDE_vTaskSuspend == 1 )
	if (*pxTicksToWait == portMAX_DELAY)
		{
			/* If INCLUDE_vTaskSuspend is set to 1 and the block time
			specified is the maximum block time then the task should block
			indefinitely, and therefore never time out. */
			xReturn = pdFALSE;
		}
		else
#endif

if ((xNumOfOverflows != pxTimeOut->xOverflowCount) && (xConstTickCount >= pxTimeOut->xTimeOnEntering)) /*lint !e525 Indentation preferred as is to make code within pre-processor directives clearer. */
		{
			/* The tick count is greater than the time at which
			vTaskSetTimeout() was called, but has also overflowed since
			vTaskSetTimeOut() was called.  It must have wrapped all the way
			around and gone past again. This passed since vTaskSetTimeout()
			was called. */
			xReturn = pdTRUE;
		}
		else if(xElapsedTime < *pxTicksToWait) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */
		{
			/* Not a genuine timeout. Adjust parameters for time remaining. */
			*pxTicksToWait -= xElapsedTime;
			vTaskInternalSetTimeOutState(pxTimeOut);
			xReturn = pdFALSE;
		}
		else
		{
			*pxTicksToWait = 0;
			xReturn = pdTRUE;
		}
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);

	return xReturn;
}

configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H setted to 1

but when i try to use ‘vTaskSetTimeOutStateFromISR(…)’ I get the following error:
vTaskSetTimeOutStateFromISR(…) was not declared in this scope

edit

Problem solved, I forgot to write #include “freertos_tasks_c_additions.h” in the file where I’m going to use my APIs

In any case, is the procedure I wrote to add new APIs valid? do you have any suggestions to improve this implementation?

parmi wrote on Monday, July 08, 2019:

no, I agree that when I compile it doesn’t find the “xNumOfOverflows” and “xTickCount” variables.
I suspect the way I implemented these APIs is incorrect.

Any tips?

Thanks.

richard_damon wrote on Monday, July 08, 2019:

If you define configINCLUDE_FREERTOS_TASK_C_ADDITIONS 1 in FreeRTOS config, then your FreeRTOS_Task_C_Additions.h file is included a part of Task.c, that means that it won’t need to include FreeRTOS.h or Tasks.h headers, as they are already available. You likely want it to include some other FreeRTOS_Additions_API.h that the callers of your new functions also include to provide declerations of the functions. (If you are using C++, don’t forget the extern “C” stuff in the API header). Your other code does NOT include FreeRTOS_Task_C_Additions.h

parmi wrote on Wednesday, July 10, 2019:

But if I don’t include the files ‘FreeRTOS.h’ and ‘task.h’, the types ‘BaseType_t’ and ‘TimeOut_t’ are not found.
unknown type name ‘BaseType_t’
unknown type name ‘TimeOut_t’

richarddamon wrote on Wednesday, July 10, 2019:

For your code in freertos_tasks_c_additions.h, those types will have been define by task.c before it includes freertos_tasks_c_additions.h, so they will not be an issue. YOU should NEVER have in include for freertos_tasks_c_additions.h in your own code, so those definitions should be ok. If you include the file in some other file, then it won’t have access to private internals of tack.c, so you can’t do that.

parmi wrote on Wednesday, July 10, 2019:

I managed to solve most minor problems.
The main problem is that I cannot access the variables ‘xTickCount’ and ‘xNumOfOverflows’ from my interrupt safe API “MyAPIFromISR(…)” declared in the file ‘freertos_tasks_c_addtions.h’, because ‘xTickCount’ and ‘xNumOfOverflows’ are declared as ‘static’ in ‘task.c’.
I think the only way to fix it is to add my API directly into FreeRTOS’s “task.c” file.

richarddamon wrote on Wednesday, July 10, 2019:

That is the whole purpose of freertos_tasks_c_additions.h, it is code that you provide that is automatically included into tasks.c when you define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H as 1 in FreeRTOSConfig.h

Even though it has a .h extension, think of it as a .c file for your program. (I personally might have called it something like freertos_tasks_c_additions.c to be clearer that it shouldn’t be included elsewhere (but then you do need to make sure it isn’t compiled as an indendant file also)

Code in freertos_tasks_c_additions.h, since it is include by tasks.c, has full access to all the variables and defintions inside tasks.c. You add your code there, and it effectively gets added to tasks.c without needing to actually change the file. Because the code in there wants to see the private internals of tasks.c, it can’t be included elsewhere in your system. You will need to have a header file to provide the needed declerations of the functions in that file so other files can call them.

parmi wrote on Wednesday, July 10, 2019:

Thanks for the explanation, but I can’t understand.
Can you show me an example?

FreeRTOSConfig.h

//...
#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H	1
//...

freertos_tasks_c_additions.h

#ifndef INC_FREERTOS_TASKS_C_ADDITIONS_H
#define INC_FREERTOS_TASKS_C_ADDITIONS_H

#ifdef __cplusplus
extern "C" {
#endif

void MyAPIFromISR()
{
	UBaseType_t uxSavedInterruptStatus;
	
	configASSERT(pxTimeOut);
	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
        BaseType_t x = xNumOfOverflows;  //[Clang IntelliSense] Error: use of undeclared identifier 'xNumOfOverflows'
        TickType_t y = xTickCount;  //[Clang IntelliSense] Error: use of undeclared identifier 'xTickCount'
	}
    portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
}

#ifdef __cplusplus
}
#endif
#endif // !INC_FREERTOS_TASKS_C_ADDITIONS_H

user_code.cpp

#include <FreeRTOS.h>
#include <task.h>

void test()
{
	TimeOut_t temp;
	
	vTaskSetTimeOutStateFromISR(&temp);  //'vTaskSetTimeOutStateFromISR' was not declared in this scope
}

richard_damon wrote on Wednesday, July 10, 2019:

Just like the functions in tasks.c have declerations in task.h, you need to create a header file (other than freertos_additions_tasks_c.h) to put those declerations in, and THAT file should be included in your user code (and in freertos_additions_tasks_c.h) to provide the needed declaration.

It would have something like:

#ifndef FREERTOS_ADDITIONS_TASK_C_API_H
#define FREERTOS_ADDITIONS_TASK_C_API_H 1

#ifdef __cplusplus
extern "C" {
#endif

extern void vTaskSetTimeOutStateFromISR(TimeOut_t *);

#ifdef __cplusplus
}
#endif

#endif

and then in freertos_additions_tasks_c.h would have the implementation of that function that you have created, and should also include the above header file to confirm the definition matches the decleration.

parmi wrote on Tuesday, August 20, 2019:

ok thanks a lot for the help, following your advice I solved the problem.

FreeRTOSConfig.h

//...
#define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H	1
//...

freertos_tasks_c_additions_api.h

#ifndef FREERTOS_TASK_C_ADDITIONS_API_H
#define FREERTOS_TASK_C_ADDITIONS_API_H

#ifdef __cplusplus
extern "C" {
#endif
	
BaseType_t xTaskCheckForTimeOut2(TimeOut_t * const pxTimeOut, TimeOut_t * const pxTimeOut2, TickType_t const pxTicksToWait);

void vTaskSetTimeOutStateFromISR(TimeOut_t*);

BaseType_t xTaskCheckForTimeOutFromISR(TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait);

#ifdef __cplusplus
}
#endif

#endif

freertos_tasks_c_additions.h

#ifndef INC_FREERTOS_TASKS_C_ADDITIONS_H
#define INC_FREERTOS_TASKS_C_ADDITIONS_H

#ifdef __cplusplus
extern "C" {
#endif

void vTaskSetTimeOutStateFromISR(TimeOut_t * const pxTimeOut)
{
	UBaseType_t uxSavedInterruptStatus;
	
	configASSERT(pxTimeOut);
	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		pxTimeOut->xTimeOnEntering = xTickCount;
		pxTimeOut->xOverflowCount = xNumOfOverflows;
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
}

BaseType_t xTaskCheckForTimeOutFromISR(TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait)
{
	BaseType_t xReturn;
	UBaseType_t uxSavedInterruptStatus;
	
	configASSERT(pxTimeOut);
	configASSERT(pxTicksToWait);
	
	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{		
		/* Minor optimisation.  The tick count cannot change in this block. */
		const TickType_t xConstTickCount = xTickCount;
		const TickType_t xElapsedTime = xConstTickCount - pxTimeOut->xTimeOnEntering;

#if( INCLUDE_xTaskAbortDelay == 1 )
		if (pxCurrentTCB->ucDelayAborted != pdFALSE)
		{
			/* The delay was aborted, which is not the same as a time out,
			but has the same result. */
			pxCurrentTCB->ucDelayAborted = pdFALSE;
			xReturn = pdTRUE;
		}
		else
#endif

#if ( INCLUDE_vTaskSuspend == 1 )
		if (*pxTicksToWait == portMAX_DELAY)
		{
			/* If INCLUDE_vTaskSuspend is set to 1 and the block time
			specified is the maximum block time then the task should block
			indefinitely, and therefore never time out. */
			xReturn = pdFALSE;
		}
		else
#endif
		if ((xNumOfOverflows != pxTimeOut->xOverflowCount) && (xConstTickCount >= pxTimeOut->xTimeOnEntering)) /*lint !e525 Indentation preferred as is to make code within pre-processor directives clearer. */
		{
			/* The tick count is greater than the time at which
			vTaskSetTimeout() was called, but has also overflowed since
			vTaskSetTimeOut() was called.  It must have wrapped all the way
			around and gone past again. This passed since vTaskSetTimeout()
			was called. */
			xReturn = pdTRUE;
		}
		else if(xElapsedTime < *pxTicksToWait) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */
		{
			/* Not a genuine timeout. Adjust parameters for time remaining. */
			*pxTicksToWait -= xElapsedTime;
			vTaskInternalSetTimeOutState(pxTimeOut);
			xReturn = pdFALSE;
		}
		else
		{
			*pxTicksToWait = 0;
			xReturn = pdTRUE;
		}
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);

	return xReturn;
}

#ifdef __cplusplus
}
#endif

#endif // !INC_FREERTOS_TASKS_C_ADDITIONS_H

my_code.cpp

//...
#include "freertos_tasks_c_additions_api.h"
//...