Enable MSVC Port to leave the prvProcessSimulatedInterrupts loop when scheduler is stopped (#728)
* allow to leave loop
* add missing brace
* Code review suggestions
Signed-off-by: Gaurav Aggarwal <aggarg@amazon.com>
---------
Signed-off-by: Gaurav Aggarwal <aggarg@amazon.com>
Co-authored-by: Gaurav-Aggarwal-AWS <33462878+aggarg@users.noreply.github.com>
Co-authored-by: Gaurav Aggarwal <aggarg@amazon.com>
diff --git a/portable/MSVC-MingW/port.c b/portable/MSVC-MingW/port.c
index f39f0ec..c85cfc1 100644
--- a/portable/MSVC-MingW/port.c
+++ b/portable/MSVC-MingW/port.c
@@ -340,6 +340,9 @@
/* Start the first task. */
ResumeThread( pxThreadState->pvThread );
+ /* The scheduler is now running. */
+ xPortRunning = pdTRUE;
+
/* Handle all simulated interrupts - including yield requests and
simulated ticks. */
prvProcessSimulatedInterrupts();
@@ -376,6 +379,8 @@
ThreadState_t *pxThreadState;
void *pvObjectList[ 2 ];
CONTEXT xContext;
+DWORD xWinApiResult;
+const DWORD xTimeoutMilliseconds = 1000;
/* Going to block on the mutex that ensured exclusive access to the simulated
interrupt objects, and the event that signals that a simulated interrupt
@@ -388,105 +393,109 @@
ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );
SetEvent( pvInterruptEvent );
- xPortRunning = pdTRUE;
-
- for(;;)
+ while( xPortRunning == pdTRUE )
{
xInsideInterrupt = pdFALSE;
- WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, INFINITE );
- /* Cannot be in a critical section to get here. Tasks that exit a
- critical section will block on a yield mutex to wait for an interrupt to
- process if an interrupt was set pending while the task was inside the
- critical section. xInsideInterrupt prevents interrupts that contain
- critical sections from doing the same. */
- xInsideInterrupt = pdTRUE;
+ /* Wait with timeout so that we can exit from this loop when
+ * the scheduler is stopped by calling vPortEndScheduler. */
+ xWinApiResult = WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, xTimeoutMilliseconds );
- /* Used to indicate whether the simulated interrupt processing has
- necessitated a context switch to another task/thread. */
- ulSwitchRequired = pdFALSE;
-
- /* 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++ )
+ if( xWinApiResult != WAIT_TIMEOUT )
{
- /* Is the simulated interrupt pending? */
- if( ( ulPendingInterrupts & ( 1UL << i ) ) != 0 )
+ /* Cannot be in a critical section to get here. Tasks that exit a
+ critical section will block on a yield mutex to wait for an interrupt to
+ process if an interrupt was set pending while the task was inside the
+ critical section. xInsideInterrupt prevents interrupts that contain
+ critical sections from doing the same. */
+ xInsideInterrupt = pdTRUE;
+
+ /* Used to indicate whether the simulated interrupt processing has
+ necessitated a context switch to another task/thread. */
+ ulSwitchRequired = pdFALSE;
+
+ /* 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 a handler installed? */
- if( ulIsrHandler[ i ] != NULL )
+ /* Is the simulated interrupt pending? */
+ if( ( ulPendingInterrupts & ( 1UL << i ) ) != 0 )
{
- /* Run the actual handler. Handlers return pdTRUE if they
- necessitate a context switch. */
- if( ulIsrHandler[ i ]() != pdFALSE )
+ /* Is a handler installed? */
+ if( ulIsrHandler[ i ] != NULL )
{
- /* A bit mask is used purely to help debugging. */
- ulSwitchRequired |= ( 1 << i );
+ /* Run the actual handler. Handlers return pdTRUE if they
+ necessitate a context switch. */
+ if( ulIsrHandler[ i ]() != pdFALSE )
+ {
+ /* A bit mask is used purely to help debugging. */
+ ulSwitchRequired |= ( 1 << i );
+ }
}
+
+ /* Clear the interrupt pending bit. */
+ ulPendingInterrupts &= ~( 1UL << i );
}
-
- /* Clear the interrupt pending bit. */
- ulPendingInterrupts &= ~( 1UL << i );
}
- }
- if( ulSwitchRequired != pdFALSE )
- {
- void *pvOldCurrentTCB;
-
- pvOldCurrentTCB = pxCurrentTCB;
-
- /* Select the next task to run. */
- vTaskSwitchContext();
-
- /* If the task selected to enter the running state is not the task
- that is already in the running state. */
- if( pvOldCurrentTCB != pxCurrentTCB )
+ if( ulSwitchRequired != pdFALSE )
{
- /* Suspend the old thread. In the cases where the (simulated)
- interrupt is asynchronous (tick event swapping a task out rather
- than a task blocking or yielding) it doesn't matter if the
- 'suspend' operation doesn't take effect immediately - if it
- doesn't it would just be like the interrupt occurring slightly
- later. In cases where the yield was caused by a task blocking
- or yielding then the task will block on a yield event after the
- yield operation in case the 'suspend' operation doesn't take
- effect immediately. */
- pxThreadState = ( ThreadState_t *) *( ( size_t * ) pvOldCurrentTCB );
- SuspendThread( pxThreadState->pvThread );
+ void *pvOldCurrentTCB;
- /* 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. Experimentation shows that these two lines don't appear
- to do anything now, but according to
- https://devblogs.microsoft.com/oldnewthing/20150205-00/?p=44743
- they do - so as they do not harm (slight run-time hit). */
- xContext.ContextFlags = CONTEXT_INTEGER;
- ( void ) GetThreadContext( pxThreadState->pvThread, &xContext );
+ pvOldCurrentTCB = pxCurrentTCB;
- /* Obtain the state of the task now selected to enter the
- Running state. */
- pxThreadState = ( ThreadState_t * ) ( *( size_t *) pxCurrentTCB );
+ /* Select the next task to run. */
+ vTaskSwitchContext();
- /* pxThreadState->pvThread can be NULL if the task deleted
- itself - but a deleted task should never be resumed here. */
- configASSERT( pxThreadState->pvThread != NULL );
- ResumeThread( pxThreadState->pvThread );
+ /* If the task selected to enter the running state is not the task
+ that is already in the running state. */
+ if( pvOldCurrentTCB != pxCurrentTCB )
+ {
+ /* Suspend the old thread. In the cases where the (simulated)
+ interrupt is asynchronous (tick event swapping a task out rather
+ than a task blocking or yielding) it doesn't matter if the
+ 'suspend' operation doesn't take effect immediately - if it
+ doesn't it would just be like the interrupt occurring slightly
+ later. In cases where the yield was caused by a task blocking
+ or yielding then the task will block on a yield event after the
+ yield operation in case the 'suspend' operation doesn't take
+ effect immediately. */
+ pxThreadState = ( ThreadState_t *) *( ( size_t * ) pvOldCurrentTCB );
+ 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. Experimentation shows that these two lines don't appear
+ to do anything now, but according to
+ https://devblogs.microsoft.com/oldnewthing/20150205-00/?p=44743
+ they do - so as they do not harm (slight run-time hit). */
+ xContext.ContextFlags = CONTEXT_INTEGER;
+ ( void ) GetThreadContext( pxThreadState->pvThread, &xContext );
+
+ /* Obtain the state of the task now selected to enter the
+ Running state. */
+ pxThreadState = ( ThreadState_t * ) ( *( size_t *) pxCurrentTCB );
+
+ /* pxThreadState->pvThread can be NULL if the task deleted
+ itself - but a deleted task should never be resumed here. */
+ configASSERT( pxThreadState->pvThread != NULL );
+ ResumeThread( pxThreadState->pvThread );
+ }
}
- }
- /* If the thread that is about to be resumed stopped running
- because it yielded then it will wait on an event when it resumed
- (to ensure it does not continue running after the call to
- SuspendThread() above as SuspendThread() is asynchronous).
- Signal the event to ensure the thread can proceed now it is
- valid for it to do so. Signaling the event is benign in the case that
- the task was switched out asynchronously by an interrupt as the event
- is reset before the task blocks on it. */
- pxThreadState = ( ThreadState_t * ) ( *( size_t *) pxCurrentTCB );
- SetEvent( pxThreadState->pvYieldEvent );
- ReleaseMutex( pvInterruptEventMutex );
+ /* If the thread that is about to be resumed stopped running
+ because it yielded then it will wait on an event when it resumed
+ (to ensure it does not continue running after the call to
+ SuspendThread() above as SuspendThread() is asynchronous).
+ Signal the event to ensure the thread can proceed now it is
+ valid for it to do so. Signaling the event is benign in the case that
+ the task was switched out asynchronously by an interrupt as the event
+ is reset before the task blocks on it. */
+ pxThreadState = ( ThreadState_t * ) ( *( size_t *) pxCurrentTCB );
+ SetEvent( pxThreadState->pvYieldEvent );
+ ReleaseMutex( pvInterruptEventMutex );
+ }
}
}
/*-----------------------------------------------------------*/