daskew wrote on Friday, December 04, 2015:
Looks like we were running 8.2.1. I’ve just updated to 8.2.3, so I now have the code change you referenced above.
I do understand your comments that processor affinity along with critical sections should protect FreeRTOS data structures. However, it looks like the FromISR versions of FreeRTOS functions do not enter a critical section.
The way my project is using FreeRTOS may be leading to issues. I have created a very simple OS layer in my product. One example of an OS function is a function called osQueuePost() that posts to a queue. This function performs a check to determine if it is being called from an ISR or not and calls either xQueueSendFromISR() or xQueueSend() depending upon context.
The way this is implemented on an ARM processor, is a register is read to determine whether we are within an ISR or not. On our windows port, we have a global variable that gets set indicating whether an ISR is executing or not.
Here is one theory about what may be going on:
- Application task is running
- Interrupt comes in and the interrupt handler sets the IN_ISR global variable
- Interrupt handler then calls osQueuePost() which calls xQueueSendFromISR()
- At the same time as this is executing, windows does a context switch and schedules the application task. The application task calls osQueuePost(). Because the global variable IN_ISR is set to true, the xQueueSendFromISR() API is called.
- At this point, there are two instances of xQueueSendFromISR() running at the same time.
My osQueuePost() function assumes that we are running a single core processor and that if an ISR is running, application code will not be running as we only have one CPU execution unit. The ISR will set the IN_ISR global variable run to completion, then clear the IN_ISR global. No matter what the application task is doing at this time when it is interrupted, it will call the appropriate FreeRTOS API.
To prevent this, it made sense to me to modify the Windows FreeRTOS port to do the following in the for loop inside :
for(;;)
{
DWORD result = WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, INFINITE );
/* Used to indicate whether the simulated interrupt processing has
necessitated a context switch to another task/thread. */
ulSwitchRequired = pdFALSE;
/* Suspend the running thread. */
pxThreadState = (xThreadState *)*((size_t *)pxCurrentTCB);
SuspendThread(pxThreadState->pvThread);
/* Ensure the thread is actually suspended by performing a
synchronous operation that can only complete when the thread is
actually suspended. The below code asks for dummy register
data. */
xContext.ContextFlags = CONTEXT_INTEGER;
(void)GetThreadContext(pxThreadState->pvThread, &xContext);
/* For each interrupt we are interested in processing, each of which is
represented by a bit in the 32bit ulPendingInterrupts variable. */
for( i = 0; i < portMAX_INTERRUPTS; i++ )
{
/* Is the simulated interrupt pending? */
if( ulPendingInterrupts & ( 1UL << i ) )
{
/* Is a handler installed? */
if( ulIsrHandler[ i ] != NULL )
{
/* Run the actual handler. */
if( ulIsrHandler[ i ]() != pdFALSE )
{
ulSwitchRequired |= ( 1 << i );
}
}
/* Clear the interrupt pending bit. */
ulPendingInterrupts &= ~( 1UL << i );
}
}
if( ulSwitchRequired != pdFALSE )
{
/* Select the next task to run. */
vTaskSwitchContext();
}
/* Obtain the state of the task now selected to enter the
Running state. */
pxThreadState = (xThreadState *)(*(size_t *)pxCurrentTCB);
ResumeThread(pxThreadState->pvThread);
ReleaseMutex( pvInterruptEventMutex );
}
Here is my osQueuePost function:
COMMON_STATUS osQueuePost( OS_Queue_Handle_t handle, void *item, uint32_t timeoutMsecs )
{
COMMON_STATUS status = COMMON_STATUS_ERROR;
ASSERT(handle != NULL);
ASSERT(item != NULL);
if ((handle != NULL) && (item != NULL))
{
// FreeRTOS uses different interfaces to post a queue item if you are in an interrupt
if (bspCPUInHandler())
{
BaseType_t yieldRequired;
// In an interrupt context
BaseType_t osResult = xQueueSendFromISR((QueueHandle_t)handle, item, &yieldRequired);
if (osResult == pdTRUE)
{
status = COMMON_STATUS_OK;
}
else
{
status = COMMON_STATUS_BUSY;
}
osSetYield(yieldRequired);
}
else
{
// Not in an interrupt context
do {
TickType_t ticksToWait;
if (timeoutMsecs != OS_WAIT_FOREVER)
{
ticksToWait = timeoutMsecs / portTICK_PERIOD_MS;
}
else
{
ticksToWait = portMAX_DELAY;
}
BaseType_t osResult = xQueueSend((QueueHandle_t)handle, item, ticksToWait);
if (osResult == pdTRUE)
{
status = COMMON_STATUS_OK;
break;
}
if (timeoutMsecs != OS_WAIT_FOREVER)
{
status = COMMON_STATUS_TIMEOUT;
break;
}
} while (timeoutMsecs == OS_WAIT_FOREVER);
}
}
ASSERT(status != COMMON_STATUS_ERROR);
return status;
}