]> git.sur5r.net Git - freertos/commitdiff
Tidy up Win32 port layer - include addition of new variable that prevents recursive...
authorrtel <rtel@1d2547de-c912-0410-9cb9-b8ca96c0e9e2>
Sun, 4 Aug 2019 01:14:43 +0000 (01:14 +0000)
committerrtel <rtel@1d2547de-c912-0410-9cb9-b8ca96c0e9e2>
Sun, 4 Aug 2019 01:14:43 +0000 (01:14 +0000)
git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@2713 1d2547de-c912-0410-9cb9-b8ca96c0e9e2

FreeRTOS/Source/portable/MSVC-MingW/port.c

index 2e67896d0e85c8f934f6d6fcd74700f4784a3913..396633123a66716ddb9e4d86512b650f2ceafb2b 100644 (file)
@@ -68,6 +68,15 @@ static void prvProcessSimulatedInterrupts( void );
 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
@@ -169,20 +178,22 @@ TIMECAPS xTimeCaps;
 \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
@@ -341,6 +352,7 @@ SYSTEM_INFO xSystemInfo;
 \r
 static uint32_t prvProcessYieldInterrupt( void )\r
 {\r
+       /* Always return true as this is a yield. */\r
        return pdTRUE;\r
 }\r
 /*-----------------------------------------------------------*/\r
@@ -379,8 +391,16 @@ CONTEXT xContext;
 \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
@@ -390,14 +410,16 @@ CONTEXT xContext;
                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
@@ -420,7 +442,15 @@ CONTEXT xContext;
                        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
@@ -451,7 +481,9 @@ CONTEXT xContext;
                (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
@@ -527,7 +559,7 @@ uint32_t ulErrorCode;
        /* 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
@@ -546,39 +578,32 @@ ThreadState_t *pxThreadState = ( ThreadState_t *) *( ( size_t * ) pxCurrentTCB )
 \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
@@ -626,10 +651,12 @@ int32_t lMutexNeedsReleasing, lWaitForYield = pdFALSE;
 \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
@@ -637,37 +664,29 @@ int32_t lMutexNeedsReleasing, lWaitForYield = pdFALSE;
                                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