]> git.sur5r.net Git - freertos/blobdiff - FreeRTOS/Source/portable/MSVC-MingW/port.c
Update version number to 8.1.2 after moving the defaulting of configUSE_PORT_OPTIMISE...
[freertos] / FreeRTOS / Source / portable / MSVC-MingW / port.c
index ddc419a86e9ae92d02a6594cd2b0e2754456e103..2582cd2b48f77dba08c9228185d657fef5af23c4 100644 (file)
@@ -1,5 +1,6 @@
 /*\r
-    FreeRTOS V7.5.2 - Copyright (C) 2013 Real Time Engineers Ltd.\r
+    FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd.\r
+    All rights reserved\r
 \r
     VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.\r
 \r
     the terms of the GNU General Public License (version 2) as published by the\r
     Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.\r
 \r
-    >>! NOTE: The modification to the GPL is included to allow you to distribute\r
-    >>! a combined work that includes FreeRTOS without being obliged to provide\r
-    >>! the source code for proprietary components outside of the FreeRTOS\r
-    >>! kernel.\r
+    >>!   NOTE: The modification to the GPL is included to allow you to     !<<\r
+    >>!   distribute a combined work that includes FreeRTOS without being   !<<\r
+    >>!   obliged to provide the source code for proprietary components     !<<\r
+    >>!   outside of the FreeRTOS kernel.                                   !<<\r
 \r
     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY\r
     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
 #include "task.h"\r
 #include <stdio.h>\r
 \r
-#define portMAX_INTERRUPTS                             ( ( unsigned long ) sizeof( unsigned long ) * 8UL ) /* The number of bits in an unsigned long. */\r
-#define portNO_CRITICAL_NESTING                ( ( unsigned long ) 0 )\r
+#ifdef __GNUC__\r
+       #include "mmsystem.h"\r
+#else\r
+       #pragma comment(lib, "winmm.lib")\r
+#endif\r
+\r
+#define portMAX_INTERRUPTS                             ( ( uint32_t ) sizeof( uint32_t ) * 8UL ) /* The number of bits in an uint32_t. */\r
+#define portNO_CRITICAL_NESTING                ( ( uint32_t ) 0 )\r
 \r
 /*\r
  * Created as a high priority thread, this function uses a timer to simulate\r
  * a tick interrupt being generated on an embedded target.  In this Windows\r
- * environment the timer does not achieve anything approaching real time \r
+ * environment the timer does not achieve anything approaching real time\r
  * performance though.\r
  */\r
 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter );\r
 \r
-/* \r
- * Process all the simulated interrupts - each represented by a bit in \r
+/*\r
+ * Process all the simulated interrupts - each represented by a bit in\r
  * ulPendingInterrupts variable.\r
  */\r
 static void prvProcessSimulatedInterrupts( void );\r
@@ -88,15 +95,21 @@ static void prvProcessSimulatedInterrupts( void );
  * Interrupt handlers used by the kernel itself.  These are executed from the\r
  * simulated interrupt handler thread.\r
  */\r
-static unsigned long prvProcessYieldInterrupt( void );\r
-static unsigned long prvProcessTickInterrupt( void );\r
+static uint32_t prvProcessYieldInterrupt( void );\r
+static uint32_t prvProcessTickInterrupt( void );\r
+\r
+/*\r
+ * Called when the process exits to let Windows know the high timer resolution\r
+ * is no longer required.\r
+ */\r
+static BOOL WINAPI prvEndProcess( DWORD dwCtrlType );\r
 \r
 /*-----------------------------------------------------------*/\r
 \r
 /* The WIN32 simulator runs each task in a thread.  The context switching is\r
 managed by the threads, so the task stack does not have to be managed directly,\r
 although the task stack is still used to hold an xThreadState structure this is\r
-the only thing it will ever hold.  The structure indirectly maps the task handle \r
+the only thing it will ever hold.  The structure indirectly maps the task handle\r
 to a thread handle. */\r
 typedef struct\r
 {\r
@@ -107,70 +120,94 @@ typedef struct
 \r
 /* Simulated interrupts waiting to be processed.  This is a bit mask where each\r
 bit represents one interrupt, so a maximum of 32 interrupts can be simulated. */\r
-static volatile unsigned long ulPendingInterrupts = 0UL;\r
+static volatile uint32_t ulPendingInterrupts = 0UL;\r
 \r
-/* An event used to inform the simulated interrupt processing thread (a high \r
+/* An event used to inform the simulated interrupt processing thread (a high\r
 priority thread that simulated interrupt processing) that an interrupt is\r
 pending. */\r
 static void *pvInterruptEvent = NULL;\r
 \r
-/* Mutex used to protect all the simulated interrupt variables that are accessed \r
+/* Mutex used to protect all the simulated interrupt variables that are accessed\r
 by multiple threads. */\r
 static void *pvInterruptEventMutex = NULL;\r
 \r
-/* The critical nesting count for the currently executing task.  This is \r
-initialised to a non-zero value so interrupts do not become enabled during \r
-the initialisation phase.  As each task has its own critical nesting value \r
-ulCriticalNesting will get set to zero when the first task runs.  This \r
+/* The critical nesting count for the currently executing task.  This is\r
+initialised to a non-zero value so interrupts do not become enabled during\r
+the initialisation phase.  As each task has its own critical nesting value\r
+ulCriticalNesting will get set to zero when the first task runs.  This\r
 initialisation is probably not critical in this simulated environment as the\r
-simulated interrupt handlers do not get created until the FreeRTOS scheduler is \r
+simulated interrupt handlers do not get created until the FreeRTOS scheduler is\r
 started anyway. */\r
-static unsigned long ulCriticalNesting = 9999UL;\r
+static uint32_t ulCriticalNesting = 9999UL;\r
 \r
 /* Handlers for all the simulated software interrupts.  The first two positions\r
 are used for the Yield and Tick interrupts so are handled slightly differently,\r
 all the other interrupts can be user defined. */\r
-static unsigned long (*ulIsrHandler[ portMAX_INTERRUPTS ])( void ) = { 0 };\r
+static uint32_t (*ulIsrHandler[ portMAX_INTERRUPTS ])( void ) = { 0 };\r
 \r
 /* Pointer to the TCB of the currently executing task. */\r
 extern void *pxCurrentTCB;\r
 \r
+/* Used to ensure nothing is processed during the startup sequence. */\r
+static BaseType_t xPortRunning = pdFALSE;\r
+\r
 /*-----------------------------------------------------------*/\r
 \r
 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter )\r
 {\r
-portTickType xMinimumWindowsBlockTime = ( portTickType ) 20;\r
+TickType_t xMinimumWindowsBlockTime;\r
+TIMECAPS xTimeCaps;\r
+\r
+       /* Set the timer resolution to the maximum possible. */\r
+       if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )\r
+       {\r
+               xMinimumWindowsBlockTime = ( TickType_t ) xTimeCaps.wPeriodMin;\r
+               timeBeginPeriod( xTimeCaps.wPeriodMin );\r
+\r
+               /* Register an exit handler so the timeBeginPeriod() function can be\r
+               matched with a timeEndPeriod() when the application exits. */\r
+               SetConsoleCtrlHandler( prvEndProcess, TRUE );\r
+       }\r
+       else\r
+       {\r
+               xMinimumWindowsBlockTime = ( TickType_t ) 20;\r
+       }\r
 \r
        /* Just to prevent compiler warnings. */\r
        ( void ) lpParameter;\r
 \r
        for(;;)\r
        {\r
-               /* Wait until the timer expires and we can access the simulated interrupt \r
-               variables.  *NOTE* this is not a 'real time' way of generating tick \r
-               events as the next wake time should be relative to the previous wake \r
-               time, not the time that Sleep() is called.  It is done this way to \r
-               prevent overruns in this very non real time simulated/emulated \r
+               /* Wait until the timer expires and we can access the simulated interrupt\r
+               variables.  *NOTE* this is not a 'real time' way of generating tick\r
+               events as the next wake time should be relative to the previous wake\r
+               time, not the time that Sleep() is called.  It is done this way to\r
+               prevent overruns in this very non real time simulated/emulated\r
                environment. */\r
-               if( portTICK_RATE_MS < xMinimumWindowsBlockTime )\r
+               if( portTICK_PERIOD_MS < xMinimumWindowsBlockTime )\r
                {\r
                        Sleep( xMinimumWindowsBlockTime );\r
                }\r
                else\r
                {\r
-                       Sleep( portTICK_RATE_MS );\r
+                       Sleep( portTICK_PERIOD_MS );\r
                }\r
 \r
+               configASSERT( xPortRunning );\r
+\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
+               /* The interrupt is now pending - notify the simulated interrupt\r
                handler thread. */\r
-               SetEvent( pvInterruptEvent );\r
+               if( ulCriticalNesting == 0 )\r
+               {\r
+                       SetEvent( pvInterruptEvent );\r
+               }\r
 \r
-               /* Give back the mutex so the simulated interrupt handler unblocks \r
+               /* Give back the mutex so the simulated interrupt handler unblocks\r
                and can access the interrupt handler variables. */\r
                ReleaseMutex( pvInterruptEventMutex );\r
        }\r
@@ -183,10 +220,27 @@ portTickType xMinimumWindowsBlockTime = ( portTickType ) 20;
 }\r
 /*-----------------------------------------------------------*/\r
 \r
-portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )\r
+static BOOL WINAPI prvEndProcess( DWORD dwCtrlType )\r
+{\r
+TIMECAPS xTimeCaps;\r
+\r
+       ( void ) dwCtrlType;\r
+\r
+       if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )\r
+       {\r
+               /* Match the call to timeBeginPeriod( xTimeCaps.wPeriodMin ) made when\r
+               the process started with a timeEndPeriod() as the process exits. */\r
+               timeEndPeriod( xTimeCaps.wPeriodMin );\r
+       }\r
+\r
+       return pdPASS;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )\r
 {\r
 xThreadState *pxThreadState = NULL;\r
-char *pcTopOfStack = ( char * ) pxTopOfStack;\r
+int8_t *pcTopOfStack = ( int8_t * ) pxTopOfStack;\r
 \r
        /* In this simulated case a stack is not initialised, but instead a thread\r
        is created that will execute the task being created.  The thread handles\r
@@ -198,18 +252,19 @@ char *pcTopOfStack = ( char * ) pxTopOfStack;
 \r
        /* Create the thread itself. */\r
        pxThreadState->pvThread = CreateThread( NULL, 0, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED, NULL );\r
+       configASSERT( pxThreadState->pvThread );\r
        SetThreadAffinityMask( pxThreadState->pvThread, 0x01 );\r
        SetThreadPriorityBoost( pxThreadState->pvThread, TRUE );\r
        SetThreadPriority( pxThreadState->pvThread, THREAD_PRIORITY_IDLE );\r
-       \r
-       return ( portSTACK_TYPE * ) pxThreadState;\r
+\r
+       return ( StackType_t * ) pxThreadState;\r
 }\r
 /*-----------------------------------------------------------*/\r
 \r
-portBASE_TYPE xPortStartScheduler( void )\r
+BaseType_t xPortStartScheduler( void )\r
 {\r
 void *pvHandle;\r
-long lSuccess = pdPASS;\r
+int32_t lSuccess = pdPASS;\r
 xThreadState *pxThreadState;\r
 \r
        /* Install the interrupt handlers used by the scheduler itself. */\r
@@ -226,7 +281,7 @@ xThreadState *pxThreadState;
                lSuccess = pdFAIL;\r
        }\r
 \r
-       /* Set the priority of this thread such that it is above the priority of \r
+       /* Set the priority of this thread such that it is above the priority of\r
        the threads that run tasks.  This higher priority is required to ensure\r
        simulated interrupts take priority over tasks. */\r
        pvHandle = GetCurrentThread();\r
@@ -234,7 +289,7 @@ xThreadState *pxThreadState;
        {\r
                lSuccess = pdFAIL;\r
        }\r
-       \r
+\r
        if( lSuccess == pdPASS )\r
        {\r
                if( SetThreadPriority( pvHandle, THREAD_PRIORITY_NORMAL ) == 0 )\r
@@ -248,7 +303,7 @@ xThreadState *pxThreadState;
        if( lSuccess == pdPASS )\r
        {\r
                /* Start the thread that simulates the timer peripheral to generate\r
-               tick interrupts.  The priority is set below that of the simulated \r
+               tick interrupts.  The priority is set below that of the simulated\r
                interrupt handler so the interrupt event mutex is used for the\r
                handshake / overrun protection. */\r
                pvHandle = CreateThread( NULL, 0, prvSimulatedPeripheralTimer, NULL, 0, NULL );\r
@@ -258,40 +313,41 @@ xThreadState *pxThreadState;
                        SetThreadPriorityBoost( pvHandle, TRUE );\r
                        SetThreadAffinityMask( pvHandle, 0x01 );\r
                }\r
-               \r
-               /* Start the highest priority task by obtaining its associated thread \r
+\r
+               /* Start the highest priority task by obtaining its associated thread\r
                state structure, in which is stored the thread handle. */\r
-               pxThreadState = ( xThreadState * ) *( ( unsigned long * ) pxCurrentTCB );\r
+               pxThreadState = ( xThreadState * ) *( ( uint32_t * ) pxCurrentTCB );\r
                ulCriticalNesting = portNO_CRITICAL_NESTING;\r
 \r
                /* Bump up the priority of the thread that is going to run, in the\r
-               hope that this will asist in getting the Windows thread scheduler to\r
+               hope that this will assist in getting the Windows thread scheduler to\r
                behave as an embedded engineer might expect. */\r
                ResumeThread( pxThreadState->pvThread );\r
 \r
-               /* Handle all simulated interrupts - including yield requests and \r
+               /* Handle all simulated interrupts - including yield requests and\r
                simulated ticks. */\r
                prvProcessSimulatedInterrupts();\r
-       }       \r
-       \r
-       /* Would not expect to return from prvProcessSimulatedInterrupts(), so should \r
+       }\r
+\r
+       /* Would not expect to return from prvProcessSimulatedInterrupts(), so should\r
        not get here. */\r
        return 0;\r
 }\r
 /*-----------------------------------------------------------*/\r
 \r
-static unsigned long prvProcessYieldInterrupt( void )\r
+static uint32_t prvProcessYieldInterrupt( void )\r
 {\r
        return pdTRUE;\r
 }\r
 /*-----------------------------------------------------------*/\r
 \r
-static unsigned long prvProcessTickInterrupt( void )\r
+static uint32_t prvProcessTickInterrupt( void )\r
 {\r
-unsigned long ulSwitchRequired;\r
+uint32_t ulSwitchRequired;\r
 \r
        /* Process the tick itself. */\r
-       ulSwitchRequired = ( unsigned long ) xTaskIncrementTick();\r
+       configASSERT( xPortRunning );\r
+       ulSwitchRequired = ( uint32_t ) xTaskIncrementTick();\r
 \r
        return ulSwitchRequired;\r
 }\r
@@ -299,16 +355,23 @@ unsigned long ulSwitchRequired;
 \r
 static void prvProcessSimulatedInterrupts( void )\r
 {\r
-unsigned long ulSwitchRequired, i;\r
+uint32_t ulSwitchRequired, i;\r
 xThreadState *pxThreadState;\r
 void *pvObjectList[ 2 ];\r
 \r
-       /* Going to block on the mutex that ensured exclusive access to the simulated \r
+       /* Going to block on the mutex that ensured exclusive access to the simulated\r
        interrupt objects, and the event that signals that a simulated interrupt\r
        should be processed. */\r
        pvObjectList[ 0 ] = pvInterruptEventMutex;\r
        pvObjectList[ 1 ] = pvInterruptEvent;\r
 \r
+       /* Create a pending tick to ensure the first task is started as soon as\r
+       this thread pends. */\r
+       ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );\r
+       SetEvent( pvInterruptEvent );\r
+\r
+       xPortRunning = pdTRUE;\r
+\r
        for(;;)\r
        {\r
                WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, INFINITE );\r
@@ -353,12 +416,12 @@ void *pvObjectList[ 2 ];
                        if( pvOldCurrentTCB != pxCurrentTCB )\r
                        {\r
                                /* Suspend the old thread. */\r
-                               pxThreadState = ( xThreadState *) *( ( unsigned long * ) pvOldCurrentTCB );\r
+                               pxThreadState = ( xThreadState *) *( ( uint32_t * ) pvOldCurrentTCB );\r
                                SuspendThread( pxThreadState->pvThread );\r
 \r
-                               /* Obtain the state of the task now selected to enter the \r
+                               /* Obtain the state of the task now selected to enter the\r
                                Running state. */\r
-                               pxThreadState = ( xThreadState * ) ( *( unsigned long *) pxCurrentTCB );\r
+                               pxThreadState = ( xThreadState * ) ( *( uint32_t *) pxCurrentTCB );\r
                                ResumeThread( pxThreadState->pvThread );\r
                        }\r
                }\r
@@ -371,14 +434,65 @@ void *pvObjectList[ 2 ];
 void vPortDeleteThread( void *pvTaskToDelete )\r
 {\r
 xThreadState *pxThreadState;\r
+uint32_t ulErrorCode;\r
 \r
-       WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
+       /* Remove compiler warnings if configASSERT() is not defined. */\r
+       ( void ) ulErrorCode;\r
 \r
        /* Find the handle of the thread being deleted. */\r
-       pxThreadState = ( xThreadState * ) ( *( unsigned long *) pvTaskToDelete );\r
-       TerminateThread( pxThreadState->pvThread, 0 );\r
+       pxThreadState = ( xThreadState * ) ( *( uint32_t *) pvTaskToDelete );\r
+\r
+       /* Check that the thread is still valid, it might have been closed by\r
+       vPortCloseRunningThread() - which will be the case if the task associated\r
+       with the thread originally deleted itself rather than being deleted by a\r
+       different task. */\r
+       if( pxThreadState->pvThread != NULL )\r
+       {\r
+               WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
+\r
+               ulErrorCode = TerminateThread( pxThreadState->pvThread, 0 );\r
+               configASSERT( ulErrorCode );\r
+\r
+               ulErrorCode = CloseHandle( pxThreadState->pvThread );\r
+               configASSERT( ulErrorCode );\r
 \r
-       ReleaseMutex( pvInterruptEventMutex );\r
+               ReleaseMutex( pvInterruptEventMutex );\r
+       }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vPortCloseRunningThread( void *pvTaskToDelete, volatile BaseType_t *pxPendYield )\r
+{\r
+xThreadState *pxThreadState;\r
+void *pvThread;\r
+uint32_t ulErrorCode;\r
+\r
+       /* Remove compiler warnings if configASSERT() is not defined. */\r
+       ( void ) ulErrorCode;\r
+\r
+       /* Find the handle of the thread being deleted. */\r
+       pxThreadState = ( xThreadState * ) ( *( uint32_t *) pvTaskToDelete );\r
+       pvThread = pxThreadState->pvThread;\r
+\r
+       /* Raise the Windows priority of the thread to ensure the FreeRTOS scheduler\r
+       does not run and swap it out before it is closed.  If that were to happen\r
+       the thread would never run again and effectively be a thread handle and\r
+       memory leak. */\r
+       SetThreadPriority( pvThread, THREAD_PRIORITY_ABOVE_NORMAL );\r
+\r
+       /* This function will not return, therefore a yield is set as pending to\r
+       ensure a context switch occurs away from this thread on the next tick. */\r
+       *pxPendYield = pdTRUE;\r
+\r
+       /* Mark the thread associated with this task as invalid so\r
+       vPortDeleteThread() does not try to terminate it. */\r
+       pxThreadState->pvThread = NULL;\r
+\r
+       /* Close the thread. */\r
+       ulErrorCode = CloseHandle( pvThread );\r
+       configASSERT( ulErrorCode );\r
+\r
+       ExitThread( 0 );\r
 }\r
 /*-----------------------------------------------------------*/\r
 \r
@@ -389,8 +503,10 @@ void vPortEndScheduler( void )
 }\r
 /*-----------------------------------------------------------*/\r
 \r
-void vPortGenerateSimulatedInterrupt( unsigned long ulInterruptNumber )\r
+void vPortGenerateSimulatedInterrupt( uint32_t ulInterruptNumber )\r
 {\r
+       configASSERT( xPortRunning );\r
+\r
        if( ( ulInterruptNumber < portMAX_INTERRUPTS ) && ( pvInterruptEventMutex != NULL ) )\r
        {\r
                /* Yield interrupts are processed even when critical nesting is non-zero. */\r
@@ -402,7 +518,7 @@ void vPortGenerateSimulatedInterrupt( unsigned long ulInterruptNumber )
                be in a critical section as calls to wait for mutexes are accumulative. */\r
                if( ulCriticalNesting == 0 )\r
                {\r
-                       SetEvent( pvInterruptEvent );                   \r
+                       SetEvent( pvInterruptEvent );\r
                }\r
 \r
                ReleaseMutex( pvInterruptEventMutex );\r
@@ -410,7 +526,7 @@ void vPortGenerateSimulatedInterrupt( unsigned long ulInterruptNumber )
 }\r
 /*-----------------------------------------------------------*/\r
 \r
-void vPortSetInterruptHandler( unsigned long ulInterruptNumber, unsigned long (*pvHandler)( void ) )\r
+void vPortSetInterruptHandler( uint32_t ulInterruptNumber, uint32_t (*pvHandler)( void ) )\r
 {\r
        if( ulInterruptNumber < portMAX_INTERRUPTS )\r
        {\r
@@ -430,7 +546,7 @@ void vPortSetInterruptHandler( unsigned long ulInterruptNumber, unsigned long (*
 \r
 void vPortEnterCritical( void )\r
 {\r
-       if( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED )\r
+       if( xPortRunning == pdTRUE )\r
        {\r
                /* The interrupt event mutex is held for the entire critical section,\r
                effectively disabling (simulated) interrupts. */\r
@@ -440,13 +556,13 @@ void vPortEnterCritical( void )
        else\r
        {\r
                ulCriticalNesting++;\r
-       }       \r
+       }\r
 }\r
 /*-----------------------------------------------------------*/\r
 \r
 void vPortExitCritical( void )\r
 {\r
-long lMutexNeedsReleasing;\r
+int32_t lMutexNeedsReleasing;\r
 \r
        /* The interrupt event mutex should already be held by this thread as it was\r
        obtained on entry to the critical section. */\r
@@ -459,10 +575,11 @@ long lMutexNeedsReleasing;
                {\r
                        ulCriticalNesting--;\r
 \r
-                       /* Were any interrupts set to pending while interrupts were \r
+                       /* Were any interrupts set to pending while interrupts were\r
                        (simulated) disabled? */\r
                        if( ulPendingInterrupts != 0UL )\r
                        {\r
+                               configASSERT( xPortRunning );\r
                                SetEvent( pvInterruptEvent );\r
 \r
                                /* Mutex will be released now, so does not require releasing\r
@@ -479,9 +596,13 @@ long lMutexNeedsReleasing;
                }\r
        }\r
 \r
-       if( lMutexNeedsReleasing == pdTRUE )\r
+       if( pvInterruptEventMutex != NULL )\r
        {\r
-               ReleaseMutex( pvInterruptEventMutex );\r
+               if( lMutexNeedsReleasing == pdTRUE )\r
+               {\r
+                       configASSERT( xPortRunning );\r
+                       ReleaseMutex( pvInterruptEventMutex );\r
+               }\r
        }\r
 }\r
 /*-----------------------------------------------------------*/\r