Preemption in ISR

Hi, I am working on data race detection and static analysis on FreeRTOS. I had a question regarding how ISRs work in FreeRTOS. Can a higher priority ISR preempt a lower priority running ISR? This question arises because the way the fromISR API s are implemented, if a higher priority ISR can preempt a lower priority running ISR, then, the state of the data structures can become corrupted. Is this observation correct? Thanks, Nikita

Preemption in ISR

Which port are you using? Some support interrupt nesting (Cortex-M3/4/7, RX, etc.) some don’t (MSP430, etc.).

Preemption in ISR

Thanks for the response. We are analysing the kernel library itself and not any applications. So, if the port supports interrupt nesting, then, how is the state of kernel data structures maintained to be consistent when accessed inside ISR? From what we observed, if a higher priority ISR preempts a lower priority ISR, then the state of the kernel data structures could become corrupted. Is this observation correct or not?

Preemption in ISR

A couple of ways – all of which can be seen in the code. First and most basically there are interrupt safe critical sections that mask interrupts up to a maximum system call priority level. Then there are object locks whereby communication objects can be accessed, but any context switches result from accessing the object are held pending until the object is unlocked. There is also a scheduler locking mechanism where task lists are not updated directly, but a pending list is used temporarily until such time that the scheduler is unlocked.

Preemption in ISR

Thanks for the reply. I understood the synchronization mechanisms you explained. But here is a situation I’m still unclear about. While going through the FreeRTOSv10.0.0 code, I encountered the following two fromISR API functions: xQueueReceiveFromISR and uxQueueMessagesWaitingFromISR, as defined below BaseTypet xQueueReceiveFromISR( QueueHandlet xQueue, void * const pvBuffer, BaseTypet * const pxHigherPriorityTaskWoken ) //code snippet { BaseTypet xReturn; UBaseTypet uxSavedInterruptStatus; Queuet * const pxQueue = ( Queue_t * ) xQueue; portASSERTIFINTERRUPTPRIORITYINVALID(); uxSavedInterruptStatus = portSETINTERRUPTMASKFROMISR(); { const UBaseTypet uxMessagesWaiting = pxQueue->uxMessagesWaiting; if( uxMessagesWaiting > ( UBaseTypet ) 0 ) { const int8t cRxLock = pxQueue->cRxLock; prvCopyDataFromQueue( pxQueue, pvBuffer ); //(PROBLEMATIC ACCESS) pxQueue->uxMessagesWaiting = uxMessagesWaiting – ( UBaseTypet ) 1; //some code here }
else{ //some code here } } portCLEARINTERRUPTMASKFROMISR( uxSavedInterruptStatus ); return xReturn; } and UBaseTypet uxQueueMessagesWaitingFromISR( const QueueHandlet xQueue ) //code snippet { UBaseTypet uxReturn; configASSERT( xQueue ); uxReturn = ( ( Queuet * ) xQueue )->uxMessagesWaiting; // (PROBLEMATIC ACCESS)
return uxReturn; } Now, consider the following application code snippet taken from FreeRTOS/Demo/CORTEXLPC1768GCCRowley/LPCUSB/ USBCDC.c : static void BulkIn(unsigned char bEP, unsigned char bEPStatus) { long lHigherPriorityTaskWoken = pdFALSE; // some code here if (uxQueueMessagesWaitingFromISR( …… ) == 0) { //CALL 1 // some code here return; } if( xQueueReceiveFromISR( ………. ) //CALL 2 { break; } //some code here portENDSWITCHINGISR( lHigherPriorityTaskWoken ); } Here, suppose 2 tasks are created which call the method BulkIn, then, there can be an instance of uxQueueMessagesWaitingFromISR (Call 1) and xQueueReceiveFromISR (Call 2) running simultaneously. In this case, there is a conflicting access to uxMessagesWaiting. We realize that xQueueReceiveFromISR calls portSETINTERRUPTMASKFROMISR() to prevent interrupt nesting but uxQueueMessagesWaitingFromISR does not. It can happen that while uxQueueMessagesWaitingFromISR is being executed, a call to xQueueReceiveFromISR comes which results in conflicting simultaneous accesses to uxMessagesWaiting. Hence, the value of uxMessagesWaiting might become inconsistent. Please verify whether my observation is correct or not? Thanks, Nikita

Preemption in ISR

Reading uxMessagesWaiting is atomic, so it will always get a consistant value (so if another ISR changes the queue, it might get the value before or after the other ISR acts) but it will be consistant. uxQueueMessagesWaitingFromISR() adding a critical section inside itself wouldn’t help here, as the other interrupt might occur before or after the critical section. If the ISR calling uxQueueMessagesWaitingFromISR() needs to query the ISR and then act on that, it needs to perform the critical section itself. xQueueReceiveFromISR() has such a critical section started with the portSETINTERRUPTMASKFROMISR() statement to the portCLEARINTERRUPTMASKFROMISR() so that the uxMessagesWaiting and data are always consistant if another ISR tries to get in at the same time. I don’t know the USB code well enough to know what assumptions the code is making, so I can’t say why it doesn’t need a critical section (assuming there isn’t a SET/CLEARINTERRUPTMASKFROMISR in the dropped sections, but it likely is that other things make sure it isn’t needed, like maybe kicking the interrupt if data is added to the queue.