/*\r
- * FreeRTOS Kernel V10.1.0\r
- * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ * FreeRTOS Kernel V10.2.1\r
+ * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
*\r
* Permission is hereby granted, free of charge, to any person obtaining a copy of\r
* this software and associated documentation files (the "Software"), to deal in\r
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
+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
/* Handle of the thread that executes the task. */\r
void *pvThread;\r
\r
-} xThreadState;\r
+ /* Event used to make sure the thread does not execute past a yield point\r
+ between the call to SuspendThread() to suspend the thread and the\r
+ asynchronous SuspendThread() operation actually being performed. */\r
+ void *pvYieldEvent;\r
+} ThreadState_t;\r
\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
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
started anyway. */\r
-static uint32_t ulCriticalNesting = 9999UL;\r
+static volatile 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
static uint32_t (*ulIsrHandler[ portMAX_INTERRUPTS ])( void ) = { 0 };\r
\r
/* Pointer to the TCB of the currently executing task. */\r
-extern void *pxCurrentTCB;\r
+extern void * volatile pxCurrentTCB;\r
\r
/* Used to ensure nothing is processed during the startup sequence. */\r
static BaseType_t xPortRunning = pdFALSE;\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 == 0 )\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
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )\r
{\r
-xThreadState *pxThreadState = NULL;\r
+ThreadState_t *pxThreadState = NULL;\r
int8_t *pcTopOfStack = ( int8_t * ) pxTopOfStack;\r
const SIZE_T xStackSize = 1024; /* Set the size to a small number which will get rounded up to the minimum possible. */\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
- the context switching itself. The xThreadState object is placed onto\r
+ the context switching itself. The ThreadState_t object is placed onto\r
the stack that was created for the task - so the stack buffer is still\r
used, just not in the conventional way. It will not be used for anything\r
other than holding this structure. */\r
- pxThreadState = ( xThreadState * ) ( pcTopOfStack - sizeof( xThreadState ) );\r
+ pxThreadState = ( ThreadState_t * ) ( pcTopOfStack - sizeof( ThreadState_t ) );\r
+\r
+ /* Create the event used to prevent the thread from executing past its yield\r
+ point if the SuspendThread() call that suspends the thread does not take\r
+ effect immediately (it is an asynchronous call). */\r
+ pxThreadState->pvYieldEvent = CreateEvent( NULL, /* Default security attributes. */\r
+ FALSE, /* Auto reset. */\r
+ FALSE, /* Start not signalled. */\r
+ NULL );/* No name. */\r
\r
/* Create the thread itself. */\r
pxThreadState->pvThread = CreateThread( NULL, xStackSize, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, NULL );\r
{\r
void *pvHandle = NULL;\r
int32_t lSuccess;\r
-xThreadState *pxThreadState = NULL;\r
+ThreadState_t *pxThreadState = NULL;\r
SYSTEM_INFO xSystemInfo;\r
\r
/* This port runs windows threads with extremely high priority. All the\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 * ) *( ( size_t * ) pxCurrentTCB );\r
+ pxThreadState = ( ThreadState_t * ) *( ( size_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 assist in getting the Windows thread scheduler to\r
- behave as an embedded engineer might expect. */\r
+ /* Start the first task. */\r
ResumeThread( pxThreadState->pvThread );\r
\r
/* Handle all simulated interrupts - including yield requests and\r
\r
static uint32_t prvProcessYieldInterrupt( void )\r
{\r
+ /* Always return true as this is a yield. */\r
return pdTRUE;\r
}\r
/*-----------------------------------------------------------*/\r
static void prvProcessSimulatedInterrupts( void )\r
{\r
uint32_t ulSwitchRequired, i;\r
-xThreadState *pxThreadState;\r
+ThreadState_t *pxThreadState;\r
void *pvObjectList[ 2 ];\r
CONTEXT xContext;\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 exit 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
- pxThreadState = ( xThreadState *) *( ( size_t * ) pvOldCurrentTCB );\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
/* Ensure the thread is actually suspended by performing a\r
synchronous operation that can only complete when the thread is\r
- actually suspended. The below code asks for dummy register\r
- data. */\r
+ actually suspended. The code below asks for dummy register\r
+ data. Experimentation shows that these two lines don't appear\r
+ to do anything now, but according to\r
+ https://devblogs.microsoft.com/oldnewthing/20150205-00/?p=44743\r
+ they do - so as they do not harm (slight run-time hit). */\r
xContext.ContextFlags = CONTEXT_INTEGER;\r
( void ) GetThreadContext( pxThreadState->pvThread, &xContext );\r
\r
/* Obtain the state of the task now selected to enter the\r
Running state. */\r
- pxThreadState = ( xThreadState * ) ( *( size_t *) pxCurrentTCB );\r
+ pxThreadState = ( ThreadState_t * ) ( *( size_t *) pxCurrentTCB );\r
+\r
+ /* pxThreadState->pvThread can be NULL if the task deleted\r
+ itself - but a deleted task should never be resumed here. */\r
+ configASSERT( pxThreadState->pvThread != NULL );\r
ResumeThread( pxThreadState->pvThread );\r
}\r
}\r
\r
+ /* If the thread that is about to be resumed stopped running\r
+ because it yielded then it will wait on an event when it resumed\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. 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
}\r
}\r
\r
void vPortDeleteThread( void *pvTaskToDelete )\r
{\r
-xThreadState *pxThreadState;\r
+ThreadState_t *pxThreadState;\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 * ) ( *( size_t *) pvTaskToDelete );\r
+ pxThreadState = ( ThreadState_t * ) ( *( size_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
\r
void vPortCloseRunningThread( void *pvTaskToDelete, volatile BaseType_t *pxPendYield )\r
{\r
-xThreadState *pxThreadState;\r
+ThreadState_t *pxThreadState;\r
void *pvThread;\r
uint32_t ulErrorCode;\r
\r
( void ) ulErrorCode;\r
\r
/* Find the handle of the thread being deleted. */\r
- pxThreadState = ( xThreadState * ) ( *( size_t *) pvTaskToDelete );\r
+ pxThreadState = ( ThreadState_t * ) ( *( size_t *) pvTaskToDelete );\r
pvThread = pxThreadState->pvThread;\r
\r
/* Raise the Windows priority of the thread to ensure the FreeRTOS scheduler\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
void vPortGenerateSimulatedInterrupt( uint32_t ulInterruptNumber )\r
{\r
+ThreadState_t *pxThreadState = ( ThreadState_t *) *( ( size_t * ) pxCurrentTCB );\r
+\r
configASSERT( xPortRunning );\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
- if( ulCriticalNesting == 0 )\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
+ /* 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
+ if( ulCriticalNesting == portNO_CRITICAL_NESTING )\r
+ {\r
+ /* An interrupt was pended so ensure to block to allow 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
/* The interrupt event mutex is held for the entire critical section,\r
effectively disabling (simulated) interrupts. */\r
WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
- ulCriticalNesting++;\r
- }\r
- else\r
- {\r
- ulCriticalNesting++;\r
}\r
+\r
+ ulCriticalNesting++;\r
}\r
/*-----------------------------------------------------------*/\r
\r
void vPortExitCritical( void )\r
{\r
-int32_t lMutexNeedsReleasing;\r
+int32_t lMutexNeedsReleasing, lWaitForYield = pdFALSE;\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
\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
{\r
+ ThreadState_t *pxThreadState = ( ThreadState_t *) *( ( size_t * ) pxCurrentTCB );\r
+\r
configASSERT( xPortRunning );\r
- SetEvent( pvInterruptEvent );\r
\r
- /* Mutex will be released now, so does not require releasing\r
- on function exit. */\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 pended 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 the (simulated) interrupt can\r
+ execute, so does not require releasing on function exit. */\r
lMutexNeedsReleasing = pdFALSE;\r
ReleaseMutex( pvInterruptEventMutex );\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