static uint32_t prvProcessYieldInterrupt( void );\r
static uint32_t prvProcessTickInterrupt( void );\r
\r
+/*\r
+ * Exiting a critical section will cause the calling task to block on yield\r
+ * event to wait for an interrupt to process if an interrupt was pended while\r
+ * inside the critical section. This variable protects against a recursive\r
+ * attempt to obtain pvInterruptEventMutex if a critical section is used inside\r
+ * an interrupt handler itself.\r
+ */\r
+static volatile BaseType_t xInsideInterrupt = pdFALSE;\r
+\r
/*\r
* Called when the process exits to let Windows know the high timer resolution\r
* is no longer required.\r
\r
configASSERT( xPortRunning );\r
\r
+ /* Can't proceed if in a critical section as pvInterruptEventMutex won't\r
+ be available. */\r
WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
\r
/* The timer has expired, generate the simulated tick event. */\r
ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );\r
\r
/* The interrupt is now pending - notify the simulated interrupt\r
- handler thread. */\r
- if( ulCriticalNesting == portNO_CRITICAL_NESTING )\r
- {\r
- SetEvent( pvInterruptEvent );\r
- }\r
+ handler thread. Must be outside of a critical section to get here so\r
+ the handler thread can execute immediately pvInterruptEventMutex is\r
+ released. */\r
+ configASSERT( ulCriticalNesting == 0UL );\r
+ SetEvent( pvInterruptEvent );\r
\r
/* Give back the mutex so the simulated interrupt handler unblocks\r
- and can access the interrupt handler variables. */\r
+ and can access the interrupt handler variables. */\r
ReleaseMutex( pvInterruptEventMutex );\r
}\r
\r
\r
static uint32_t prvProcessYieldInterrupt( void )\r
{\r
+ /* Always return true as this is a yield. */\r
return pdTRUE;\r
}\r
/*-----------------------------------------------------------*/\r
\r
for(;;)\r
{\r
+ xInsideInterrupt = pdFALSE;\r
WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, INFINITE );\r
\r
+ /* /* Cannot be in a critical section to get here. Tasks that exist a\r
+ critical section will block on a yield mutex to wait for an interrupt to\r
+ process if an interrupt was set pending while the task was inside the\r
+ critical section. xInsideInterrupt prevents interrupts that contain\r
+ critical sections from doing the same. */\r
+ xInsideInterrupt = pdTRUE;\r
+\r
/* Used to indicate whether the simulated interrupt processing has\r
necessitated a context switch to another task/thread. */\r
ulSwitchRequired = pdFALSE;\r
for( i = 0; i < portMAX_INTERRUPTS; i++ )\r
{\r
/* Is the simulated interrupt pending? */\r
- if( ulPendingInterrupts & ( 1UL << i ) )\r
+ if( ( ulPendingInterrupts & ( 1UL << i ) ) != 0 )\r
{\r
/* Is a handler installed? */\r
if( ulIsrHandler[ i ] != NULL )\r
{\r
- /* Run the actual handler. */\r
+ /* Run the actual handler. Handlers return pdTRUE if they\r
+ necessitate a context switch. */\r
if( ulIsrHandler[ i ]() != pdFALSE )\r
{\r
+ /* A bit mask is used purely to help debugging. */\r
ulSwitchRequired |= ( 1 << i );\r
}\r
}\r
that is already in the running state. */\r
if( pvOldCurrentTCB != pxCurrentTCB )\r
{\r
- /* Suspend the old thread. */\r
+ /* Suspend the old thread. In the cases where the (simulated)\r
+ interrupt is asynchronous (tick event swapping a task out rather\r
+ than a task blocking or yielding) it doesn't matter if the\r
+ 'suspend' operation doesn't take effect immediately - if it\r
+ doesn't it would just be like the interrupt occurring slightly\r
+ later. In cases where the yield was caused by a task blocking\r
+ or yielding then the task will block on a yield event after the\r
+ yield operation in case the 'suspend' operation doesn't take\r
+ effect immediately. */\r
pxThreadState = ( ThreadState_t *) *( ( size_t * ) pvOldCurrentTCB );\r
SuspendThread( pxThreadState->pvThread );\r
\r
(to ensure it does not continue running after the call to\r
SuspendThread() above as SuspendThread() is asynchronous).\r
Signal the event to ensure the thread can proceed now it is\r
- valid for it to do so. */\r
+ valid for it to do so. Signaling the event is benign in the case that\r
+ the task was switched out asynchronously by an interrupt as the event\r
+ is reset before the task blocks on it. */\r
pxThreadState = ( ThreadState_t * ) ( *( size_t *) pxCurrentTCB );\r
SetEvent( pxThreadState->pvYieldEvent );\r
ReleaseMutex( pvInterruptEventMutex );\r
/* This is called from a critical section, which must be exited before the\r
thread stops. */\r
taskEXIT_CRITICAL();\r
-\r
+ CloseHandle( pxThreadState->pvYieldEvent );\r
ExitThread( 0 );\r
}\r
/*-----------------------------------------------------------*/\r
\r
if( ( ulInterruptNumber < portMAX_INTERRUPTS ) && ( pvInterruptEventMutex != NULL ) )\r
{\r
- /* Yield interrupts are processed even when critical nesting is\r
- non-zero. */\r
WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
ulPendingInterrupts |= ( 1 << ulInterruptNumber );\r
\r
/* The simulated interrupt is now held pending, but don't actually\r
process it yet if this call is within a critical section. It is\r
possible for this to be in a critical section as calls to wait for\r
- mutexes are accumulative. */\r
+ mutexes are accumulative. If in a critical section then the event\r
+ will get set when the critical section nesting count is wound back\r
+ down to zero. */\r
if( ulCriticalNesting == portNO_CRITICAL_NESTING )\r
{\r
SetEvent( pvInterruptEvent );\r
\r
- if( ulInterruptNumber == portINTERRUPT_YIELD )\r
- {\r
- /* Going to wait for an event - make sure the event is not already\r
- signaled. */\r
- ResetEvent( pxThreadState->pvYieldEvent );\r
- }\r
+ /* Going to wait for an event - make sure the event is not already\r
+ signaled. */\r
+ ResetEvent( pxThreadState->pvYieldEvent );\r
}\r
\r
ReleaseMutex( pvInterruptEventMutex );\r
-\r
if( ulCriticalNesting == portNO_CRITICAL_NESTING )\r
{\r
- if( ulInterruptNumber == portINTERRUPT_YIELD )\r
- {\r
- /* The task was yielding so will be suspended however the\r
- SuspendThread() function is asynchronous so this event is used to\r
- prevent the task executing further if SuspendThread() does not take\r
- effect immediately. */\r
- WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );\r
- }\r
+ /* An interrupt was pended so ensure to block to alow it to\r
+ execute. In most cases the (simulated) interrupt will have\r
+ executed before the next line is reached - so this is just to make\r
+ sure. */\r
+ WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );\r
}\r
}\r
}\r
\r
if( ulCriticalNesting > portNO_CRITICAL_NESTING )\r
{\r
- if( ulCriticalNesting == ( portNO_CRITICAL_NESTING + 1 ) )\r
- {\r
- ulCriticalNesting--;\r
+ ulCriticalNesting--;\r
\r
+ /* Don't need to wait for any pending interrupts to execute if the\r
+ critical section was exited from inside an interrupt. */\r
+ if( ( ulCriticalNesting == portNO_CRITICAL_NESTING ) && ( xInsideInterrupt == pdFALSE ) )\r
+ {\r
/* Were any interrupts set to pending while interrupts were\r
(simulated) disabled? */\r
if( ulPendingInterrupts != 0UL )\r
ThreadState_t *pxThreadState = ( ThreadState_t *) *( ( size_t * ) pxCurrentTCB );\r
\r
configASSERT( xPortRunning );\r
- SetEvent( pvInterruptEvent );\r
\r
- if( ulPendingInterrupts != 0 )\r
- {\r
- /* Going to wait for an event - make sure the event is not already\r
- signaled. */\r
- ResetEvent( pxThreadState->pvYieldEvent );\r
- lWaitForYield = pdTRUE;\r
- }\r
+ /* The interrupt won't actually executed until\r
+ pvInterruptEventMutex is released as it waits on both\r
+ pvInterruptEventMutex and pvInterruptEvent.\r
+ pvInterruptEvent is only set when the simulated\r
+ interrupt is pendeded if the interrupt is pended\r
+ from outside a critical section - hence it is set\r
+ here. */\r
+ SetEvent( pvInterruptEvent );\r
+ /* The calling task is going to wait for an event to ensure the\r
+ interrupt that is pending executes immediately after the\r
+ critical section is exited - so make sure the event is not\r
+ already signaled. */\r
+ ResetEvent( pxThreadState->pvYieldEvent );\r
+ lWaitForYield = pdTRUE;\r
\r
- /* Mutex will be released now, so does not require releasing\r
- on function exit. */\r
+ /* Mutex will be released now so the (simulated) interrupt can\r
+ execute, so does not require releasing on function exit. */\r
lMutexNeedsReleasing = pdFALSE;\r
ReleaseMutex( pvInterruptEventMutex );\r
-\r
- if( lWaitForYield != pdFALSE )\r
- {\r
- /* The task was yielding so will be suspended however the\r
- SuspendThread() function is asynchronous so this event is\r
- used to prevent the task executing further if\r
- SuspendThread() does not take effect immediately. */\r
- WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );\r
- }\r
+ WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );\r
}\r
}\r
- else\r
- {\r
- /* Tick interrupts will still not be processed as the critical\r
- nesting depth will not be zero. */\r
- ulCriticalNesting--;\r
- }\r
}\r
\r
if( pvInterruptEventMutex != NULL )\r