#include "task.h"\r
#include <stdio.h>\r
\r
-\r
-\r
-\r
-\r
-typedef struct xTaskControlBlock\r
-{\r
- volatile portSTACK_TYPE *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE STRUCT. */\r
-\r
- #if ( portUSING_MPU_WRAPPERS == 1 )\r
- xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE STRUCT. */\r
- #endif \r
- \r
- xListItem xGenericListItem; /*< List item used to place the TCB in ready and blocked queues. */\r
- xListItem xEventListItem; /*< List item used to place the TCB in event lists. */\r
- unsigned portBASE_TYPE uxPriority; /*< The priority of the task where 0 is the lowest priority. */\r
- portSTACK_TYPE *pxStack; /*< Points to the start of the stack. */\r
- signed char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */\r
-\r
- #if ( portSTACK_GROWTH > 0 )\r
- portSTACK_TYPE *pxEndOfStack; /*< Used for stack overflow checking on architectures where the stack grows up from low memory. */\r
- #endif\r
-\r
- #if ( portCRITICAL_NESTING_IN_TCB == 1 )\r
- unsigned portBASE_TYPE uxCriticalNesting;\r
- #endif\r
-\r
- #if ( configUSE_TRACE_FACILITY == 1 )\r
- unsigned portBASE_TYPE uxTCBNumber; /*< This is used for tracing the scheduler and making debugging easier only. */\r
- #endif\r
-\r
- #if ( configUSE_MUTEXES == 1 )\r
- unsigned portBASE_TYPE uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */\r
- #endif\r
-\r
- #if ( configUSE_APPLICATION_TASK_TAG == 1 )\r
- pdTASK_HOOK_CODE pxTaskTag;\r
- #endif\r
-\r
- #if ( configGENERATE_RUN_TIME_STATS == 1 )\r
- unsigned long ulRunTimeCounter; /*< Used for calculating how much CPU time each task is utilising. */\r
- #endif\r
-\r
-} xTCB;\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-FILE *pfTraceFile = NULL;\r
-//#define vPortTrace( x ) if( pfTraceFile == NULL ) pfTraceFile = fopen( "c:/temp/trace.txt", "w" ); if( pfTraceFile != NULL ) fprintf( pfTraceFile, x )\r
-#define vPortTrace( x ) ( void ) x\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
\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 real time performance though.\r
+ * environment the timer does not achieve anything approaching real time \r
+ * performance though.\r
*/\r
static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter );\r
\r
* Process all the simulated interrupts - each represented by a bit in \r
* ulPendingInterrupts variable.\r
*/\r
-static void prvProcessEvents( void );\r
+static void prvProcessSimulatedInterrupts( void );\r
+\r
+/*\r
+ * Interrupt handlers used by the kernel itself. These are executed from the\r
+ * simulated interrupt handler thread.\r
+ */\r
+static unsigned long prvProcessDeleteThreadInterrupt( void );\r
+static unsigned long prvProcessYieldInterrupt( void );\r
+static unsigned long prvProcessTickInterrupt( void );\r
\r
/*-----------------------------------------------------------*/\r
\r
to a thread handle. */\r
typedef struct\r
{\r
- portSTACK_TYPE ulCriticalNesting; /* Critical nesting count of the task. */\r
- void * pvThread; /* Handle of the thread that executes the task. */\r
-} xThreadState;\r
+ /* Handle of the thread that executes the task. */\r
+ void *pvThread;\r
\r
-/* The parameters passed to a thread when it is created. */\r
-typedef struct XPARAMS\r
-{\r
- pdTASK_CODE pxCode; /* The entry point of the task (rather than thread) code. */\r
- void *pvParameters; /* The parameters that are passed to the task (rather than thread. */\r
-} xParams;\r
+} xThreadState;\r
\r
-/* Pseudo interrupts waiting to be processed. This is a bit mask where each\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
\r
-/* An event used to inform the interrupt dispatch thread (a high priority thread\r
-that simulated interrupt processing) that an IRQ or SWI type interrupt is\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 pseudo interrupt variables that are accessed by\r
-multiple threads. */\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 main thread, which also acts as the pseudo interrupt handler. */\r
-static void *pvMainThreadAndInterrupHandler;\r
-\r
-/* Events used to manage sequencing. */\r
-static void *pvTickAcknowledgeEvent = NULL, *pvInterruptAcknowledgeEvent = 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
initialisation is probably not critical in this simulated environment as the\r
-pseudo interrupt handlers/dispatchers do not get created until the FreeRTOS\r
-scheduler is started. */\r
-static unsigned portLONG ulCriticalNesting = 9999UL;\r
+simulated interrupt handlers do not get created until the FreeRTOS scheduler is \r
+started anyway. */\r
+static unsigned long 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 void (*vIsrHandler[ portMAX_INTERRUPTS ])( void ) = { 0 };\r
+static unsigned long (*ulIsrHandler[ portMAX_INTERRUPTS ])( void ) = { 0 };\r
\r
/* Pointer to the TCB of the currently executing task. */\r
extern void *pxCurrentTCB;\r
\r
static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter )\r
{\r
-void *pvTimer;\r
-LARGE_INTEGER liDueTime;\r
-void *pvObjectList[ 2 ];\r
-const long long ll_ms_In_100ns_units = ( long long ) -1000;\r
-extern volatile unsigned long ulTicks;\r
-\r
/* Just to prevent compiler warnings. */\r
( void ) lpParameter;\r
\r
- /* The timer is created as a one shot timer even though we want it to repeat\r
- at a given frequency. This is because Windows is not a real time environment,\r
- and attempting to set a high frequency periodic timer will result in event\r
- overruns. Therefore the timer is just reset after each time the pseudo \r
- interrupt handler has processed each tick event. */\r
- pvTimer = CreateWaitableTimer( NULL, TRUE, NULL );\r
- \r
- liDueTime.QuadPart = ( long long ) portTICK_RATE_MS * ll_ms_In_100ns_units;\r
-\r
- /* Block on the timer itself and the event mutex that grants access to the \r
- interrupt variables. */\r
- pvObjectList[ 0 ] = pvInterruptEventMutex;\r
- pvObjectList[ 1 ] = pvTimer;\r
-\r
for(;;)\r
{\r
- ulTicks++;\r
-\r
- /* The timer is reset on each itteration of this loop rather than being set\r
- to function periodicallys - this is for the reasons stated in the comments\r
- where the timer is created. */\r
- vPortTrace( "prvSimulatedPeripheralTimer: Tick acked, setting new tick timer\r\n" );\r
- SetWaitableTimer( pvTimer, &liDueTime, 0, NULL, NULL, TRUE );\r
-\r
- /* Wait until the timer expires and we can access the pseudo interrupt \r
- variables. */\r
- //WaitForMultipleObjects( ( sizeof( pvObjectList ) / sizeof( void * ) ), pvObjectList, TRUE, INFINITE );\r
- WaitForSingleObject( pvTimer, INFINITE );\r
- vPortTrace( "prvSimulatedPeripheralTimer: Timer signalled, waiting interrupt event mutex\r\n" );\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
+ Sleep( portTICK_RATE_MS );\r
+\r
WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
- vPortTrace( "prvSimulatedPeripheralTimer: Got interrupt event mutex\r\n" );\r
- \r
+\r
/* The timer has expired, generate the simulated tick event. */\r
ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );\r
- if( pvInterruptEvent != NULL )\r
- {\r
- vPortTrace( "prvSimulatedPeripheralTimer: Setting interrupt event to signal tick\r\n" );\r
- SetEvent( pvInterruptEvent );\r
- }\r
\r
- /* Give back the mutex so the pseudo interrupt handler unblocks and can\r
- access the interrupt handler variables. This high priority task will then\r
- loop back round to wait for the lower priority psuedo interrupt handler \r
- thread to acknowledge the tick. */\r
- if( pvInterruptEventMutex != NULL )\r
- {\r
- vPortTrace( "prvSimulatedPeripheralTimer: Releasing interrupt event mutex so tick can be processed\r\n" );\r
- ReleaseMutex( pvInterruptEventMutex );\r
- }\r
+ /* The interrupt is now pending - notify the simulated interrupt \r
+ handler thread. */\r
+ SetEvent( pvInterruptEvent );\r
\r
- /* Wait for the previous tick to be acknowledged before resetting the timer.\r
- As mentioned above this is done to prevent timer overruns in the non real-\r
- time environment. THIS IS NOT HOW A REAL PORT SHOULD USE TIMERS! */\r
- WaitForSingleObject( pvTickAcknowledgeEvent, INFINITE );\r
+ /* Give back the mutex so the simulated interrupt handler unblocks \r
+ and can access the interrupt handler variables. */\r
+ ReleaseMutex( pvInterruptEventMutex );\r
}\r
+\r
+ #ifdef __GNUC__\r
+ /* Should never reach here - MingW complains if you leave this line out,\r
+ MSVC complains if you put it in. */\r
+ return 0;\r
+ #endif\r
}\r
/*-----------------------------------------------------------*/\r
\r
portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )\r
{\r
xThreadState *pxThreadState = NULL;\r
-xParams *pxThreadParams = ( void * ) pvPortMalloc( sizeof( xParams ) );\r
\r
- if( pxThreadParams != NULL )\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 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 * ) ( pxTopOfStack - sizeof( xThreadState ) );\r
-\r
- /* The parameters that are passed into the thread so it knows how to\r
- start the task executing. */\r
- pxThreadParams->pxCode = pxCode;\r
- pxThreadParams->pvParameters = pvParameters;\r
-\r
- /* Create the thread itself. */\r
- //pxThreadState->pvThread = ( void * ) CreateThread( NULL, 0, ( LPTHREAD_START_ROUTINE ) prvThreadEntryPoint, pxThreadParams, CREATE_SUSPENDED, NULL );\r
- pxThreadState->pvThread = ( void * ) CreateThread( NULL, 0, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED, NULL );\r
- pxThreadState->ulCriticalNesting = portNO_CRITICAL_NESTING;\r
- SetThreadPriority( pxThreadState->pvThread, THREAD_PRIORITY_IDLE );\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 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 * ) ( pxTopOfStack - sizeof( xThreadState ) );\r
+\r
+ /* Create the thread itself. */\r
+ pxThreadState->pvThread = CreateThread( NULL, 0, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED, NULL );\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
long lSuccess = pdPASS;\r
xThreadState *pxThreadState;\r
\r
- /* Set the priority of this thread such that it is above the priority of the\r
- threads that run tasks, but below the priority of the thread that generates\r
- the pseudo tick interrupts. This priority is chosen because this is the\r
- thread that actually handles the psuedo interrupts. */\r
+ /* Install the interrupt handlers used by the scheduler itself. */\r
+ vPortSetInterruptHandler( portINTERRUPT_YIELD, prvProcessYieldInterrupt );\r
+ vPortSetInterruptHandler( portINTERRUPT_TICK, prvProcessTickInterrupt );\r
+ vPortSetInterruptHandler( portINTERRUPT_DELETE_THREAD, prvProcessDeleteThreadInterrupt );\r
+\r
+ /* Create the events and mutexes that are used to synchronise all the\r
+ threads. */\r
+ pvInterruptEventMutex = CreateMutex( NULL, FALSE, NULL );\r
+ pvInterruptEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
+\r
+ if( ( pvInterruptEventMutex == NULL ) || ( pvInterruptEvent == NULL ) )\r
+ {\r
+ lSuccess = pdFAIL;\r
+ }\r
+\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
if( pvHandle == NULL )\r
{\r
\r
if( lSuccess == pdPASS )\r
{\r
- if( SetThreadPriority( pvHandle, THREAD_PRIORITY_BELOW_NORMAL ) == 0 )\r
+ if( SetThreadPriority( pvHandle, THREAD_PRIORITY_NORMAL ) == 0 )\r
{\r
lSuccess = pdFAIL;\r
}\r
+ SetThreadPriorityBoost( pvHandle, TRUE );\r
+ SetThreadAffinityMask( pvHandle, 0x01 );\r
}\r
\r
if( lSuccess == pdPASS )\r
{\r
- /* Create the events and mutexes that are used to synchronise all the\r
- threads. */\r
- pvInterruptEventMutex = CreateMutex( NULL, FALSE, NULL );\r
- pvInterruptEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
- pvTickAcknowledgeEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
- pvInterruptAcknowledgeEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
-\r
/* Start the thread that simulates the timer peripheral to generate\r
- tick interrupts. */\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
if( pvHandle != NULL )\r
{\r
- SetThreadPriority( pvHandle, THREAD_PRIORITY_ABOVE_NORMAL );\r
+ SetThreadPriority( pvHandle, THREAD_PRIORITY_BELOW_NORMAL );\r
+ SetThreadPriorityBoost( pvHandle, TRUE );\r
+ SetThreadAffinityMask( pvHandle, 0x01 );\r
}\r
\r
- /* Start the highest priority task by obtaining its associated thread state\r
- structure, in which is stored the thread handle. */\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
ulCriticalNesting = portNO_CRITICAL_NESTING;\r
\r
- vPortTrace( "Created system threads, starting task" );\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
+ behave as an embedded engineer might expect. */\r
ResumeThread( pxThreadState->pvThread );\r
+\r
+ /* Handle all simulated interrupts - including yield requests and \r
+ simulated ticks. */\r
+ prvProcessSimulatedInterrupts();\r
} \r
\r
- /* Handle all pseudo interrupts - including yield requests and simulated ticks. */\r
- prvProcessEvents();\r
-\r
- /* Would not expect to return from prvProcessEvents(), so should not get here. */\r
+ /* Would not expect to return from prvProcessSimulatedInterrupts(), so should \r
+ not get here. */\r
return 0;\r
}\r
/*-----------------------------------------------------------*/\r
\r
-static void prvProcessEvents( void )\r
+static unsigned long prvProcessDeleteThreadInterrupt( void )\r
{\r
-long lSwitchRequired, lAcknowledgeTick, lAcknowledgeInterrupt;\r
+ return pdTRUE;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static unsigned long prvProcessYieldInterrupt( void )\r
+{\r
+ return pdTRUE;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static unsigned long prvProcessTickInterrupt( void )\r
+{\r
+unsigned long ulSwitchRequired;\r
+\r
+ /* Process the tick itself. */\r
+ vTaskIncrementTick();\r
+ #if( configUSE_PREEMPTION != 0 )\r
+ {\r
+ /* A context switch is only automatically performed from the tick\r
+ interrupt if the pre-emptive scheduler is being used. */\r
+ ulSwitchRequired = pdTRUE;\r
+ }\r
+ #else\r
+ {\r
+ ulSwitchRequired = pdFALSE;\r
+ }\r
+ #endif\r
+\r
+ return ulSwitchRequired;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvProcessSimulatedInterrupts( void )\r
+{\r
+unsigned long ulSwitchRequired, i;\r
xThreadState *pxThreadState;\r
void *pvObjectList[ 2 ];\r
-unsigned long i;\r
-char cTraceBuffer[ 256 ];\r
\r
- vPortTrace( "Entering prvProcessEvents\r\n" );\r
-\r
- /* Going to block on the mutex that ensured exclusive access to the pdeudo \r
- interrupt objects, and the event that signals that an interrupt is waiting\r
- to be processed. */\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
for(;;)\r
{\r
- vPortTrace( "prvProcessEvents: Waiting for next interrupt event\r\n" );\r
WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, INFINITE );\r
- vPortTrace( "prvProcessEvents: Got interrupt event and mutex\r\n" );\r
- //vPortTrace( "prvProcessEvents: Waiting for next interrupt event\r\n" );\r
- //WaitForSingleObject( pvInterruptEvent, INFINITE );\r
- //vPortTrace( "prvProcessEvents: Waiting interrupt event mutex to access interrupt data\r\n" );\r
- //WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
\r
- lSwitchRequired = pdFALSE;\r
- lAcknowledgeTick = pdFALSE;\r
- lAcknowledgeInterrupt = pdFALSE;\r
+ /* Used to indicate whether the simulated interrupt processing has\r
+ necessitated a context switch to another task/thread. */\r
+ ulSwitchRequired = pdFALSE;\r
\r
- /* For each interrupt we are interested in processing, each of which is \r
+ /* For each interrupt we are interested in processing, each of which is\r
represented by a bit in the 32bit ulPendingInterrupts variable. */\r
for( i = 0; i < portMAX_INTERRUPTS; i++ )\r
{\r
- /* Is the pseudo interrupt pending? */\r
+ /* Is the simulated interrupt pending? */\r
if( ulPendingInterrupts & ( 1UL << i ) )\r
{\r
- switch( i )\r
+ /* Is a handler installed? */\r
+ if( ulIsrHandler[ i ] != NULL )\r
{\r
- case portINTERRUPT_YIELD:\r
-\r
- vPortTrace( "prvProcessEvents: Processing Yield\r\n" );\r
- /* Yield interrupts occur no matter what the critical nesting count. */\r
- lSwitchRequired = pdTRUE;\r
-\r
- /* Clear the interrupt pending bit. */\r
- ulPendingInterrupts &= ~( 1UL << portINTERRUPT_YIELD );\r
-\r
- lAcknowledgeInterrupt = pdTRUE;\r
- break;\r
-\r
- case portINTERRUPT_TICK:\r
- \r
- /* Tick interrupts should only be processed if the critical nesting count\r
- is zero. The critical nesting count represents the interrupt mask on \r
- real target hardware. */\r
- vPortTrace( "prvProcessEvents: Processing tick event\r\n" );\r
- if( ulCriticalNesting == 0 )\r
- {\r
- /* Process the tick itself. */\r
- vPortTrace( "prvProcessEvents: Incrementing tick\r\n" );\r
- vTaskIncrementTick();\r
- #if( configUSE_PREEMPTION != 0 )\r
- {\r
- /* A context switch is only automatically performed from the tick\r
- interrupt if the pre-emptive scheduler is being used. */\r
- lSwitchRequired = pdTRUE;\r
- }\r
- #endif\r
- \r
- lAcknowledgeTick = pdTRUE;\r
-\r
- /* Clear the interrupt pending bit. */\r
- ulPendingInterrupts &= ~( 1UL << portINTERRUPT_TICK );\r
- }\r
- break;\r
-\r
- default:\r
-\r
- /* Is a handler installed? */\r
- if( vIsrHandler[ i ] != NULL )\r
- {\r
- lSwitchRequired = pdTRUE;\r
-\r
- /* Run the actual handler. */\r
- vIsrHandler[ i ]();\r
-\r
- /* Clear the interrupt pending bit. */\r
- ulPendingInterrupts &= ~( 1UL << i );\r
-\r
- lAcknowledgeInterrupt = pdTRUE;\r
- }\r
- break;\r
+ /* Run the actual handler. */\r
+ if( ulIsrHandler[ i ]() != pdFALSE )\r
+ {\r
+ ulSwitchRequired |= ( 1 << i );\r
+ }\r
}\r
+\r
+ /* Clear the interrupt pending bit. */\r
+ ulPendingInterrupts &= ~( 1UL << i );\r
}\r
}\r
\r
- if( lSwitchRequired != pdFALSE )\r
+ if( ulSwitchRequired != pdFALSE )\r
{\r
void *pvOldCurrentTCB;\r
\r
pvOldCurrentTCB = pxCurrentTCB;\r
\r
- /* Save the state of the current thread before suspending it. */\r
- pxThreadState = ( xThreadState *) *( ( unsigned long * ) pxCurrentTCB );\r
- pxThreadState->ulCriticalNesting = ulCriticalNesting ;\r
- \r
/* Select the next task to run. */\r
vTaskSwitchContext();\r
- \r
+\r
/* If the task selected to enter the running state is not the task\r
that is already in the running state. */\r
if( pvOldCurrentTCB != pxCurrentTCB )\r
{\r
/* Suspend the old thread. */\r
- SuspendThread( pxThreadState->pvThread );\r
- sprintf( cTraceBuffer, "Event processor: suspending %s, resuming %s\r\n", ((xTCB*)pvOldCurrentTCB)->pcTaskName, ((xTCB*)pxCurrentTCB)->pcTaskName );\r
- vPortTrace( cTraceBuffer );\r
+ pxThreadState = ( xThreadState *) *( ( unsigned long * ) pvOldCurrentTCB );\r
+\r
+ if( ( ulSwitchRequired & ( 1 << portINTERRUPT_DELETE_THREAD ) ) != pdFALSE )\r
+ {\r
+ TerminateThread( pxThreadState->pvThread, 0 );\r
+ }\r
+ else\r
+ {\r
+ SuspendThread( pxThreadState->pvThread );\r
+ } \r
\r
- /* Obtain the state of the task now selected to enter the Running state. */\r
+ /* Obtain the state of the task now selected to enter the \r
+ Running state. */\r
pxThreadState = ( xThreadState * ) ( *( unsigned long *) pxCurrentTCB );\r
- ulCriticalNesting = pxThreadState->ulCriticalNesting;\r
ResumeThread( pxThreadState->pvThread );\r
}\r
}\r
\r
- /* Was a tick processed? */\r
- if( lAcknowledgeTick != pdFALSE )\r
- {\r
- vPortTrace( "prvProcessEvents: Acking tick\r\n" );\r
- SetEvent( pvTickAcknowledgeEvent );\r
- }\r
+ ReleaseMutex( pvInterruptEventMutex );\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
\r
- if( lAcknowledgeInterrupt != pdFALSE )\r
- {\r
- vPortTrace( "prvProcessEvents: Acking interrupt\r\n" );\r
- SetEvent( pvInterruptAcknowledgeEvent );\r
- }\r
+void vPortDeleteThread( void *pvTaskToDelete )\r
+{\r
+xThreadState *pxThreadState;\r
+\r
+ if( pvTaskToDelete == pxCurrentTCB )\r
+ {\r
+ /* The task is deleting itself, and so the thread that is running now\r
+ is also to be deleted. This has to be deferred until this thread is\r
+ no longer running, so its done in the simulated interrupt handler thread. */\r
+ vPortGenerateSimulatedInterrupt( portINTERRUPT_DELETE_THREAD );\r
+ }\r
+ else\r
+ {\r
+ WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
+\r
+ /* Find the handle of the thread being deleted. */\r
+ pxThreadState = ( xThreadState * ) ( *( unsigned long *) pvTaskToDelete );\r
+ TerminateThread( pxThreadState->pvThread, 0 );\r
\r
ReleaseMutex( pvInterruptEventMutex );\r
}\r
\r
void vPortEndScheduler( void )\r
{\r
+ /* This function IS NOT TESTED! */\r
+ TerminateProcess( GetCurrentProcess(), 0 );\r
}\r
/*-----------------------------------------------------------*/\r
\r
-void vPortGeneratePseudoInterrupt( unsigned long ulInterruptNumber )\r
+void vPortGenerateSimulatedInterrupt( unsigned long ulInterruptNumber )\r
{\r
+xThreadState *pxThreadState;\r
+\r
if( ( ulInterruptNumber < portMAX_INTERRUPTS ) && ( pvInterruptEventMutex != NULL ) )\r
{\r
/* Yield interrupts are processed even when critical nesting is non-zero. */\r
- if( ( ulCriticalNesting == 0 ) || ( ulInterruptNumber == portINTERRUPT_YIELD ) )\r
- {\r
- /* In case this task has just started running, reset the interrupt\r
- acknowledge event as it might have been set due to the activities\r
- of a thread that has already been executed and suspended. */\r
- ResetEvent( pvInterruptAcknowledgeEvent );\r
-\r
- WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
- ulPendingInterrupts |= ( 1 << ulInterruptNumber );\r
- vPortTrace( "vPortGeneratePseudoInterrupt: Got interrupt mutex, about to signal interrupt event\r\n" );\r
- SetEvent( pvInterruptEvent );\r
- vPortTrace( "vPortGeneratePseudoInterrupt: About to release interrupt event mutex\r\n" );\r
- ReleaseMutex( pvInterruptEventMutex );\r
- vPortTrace( "vPortGeneratePseudoInterrupt: Interrupt event mutex released, going to wait for next interrupt input\r\n" );\r
+ WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
+ ulPendingInterrupts |= ( 1 << ulInterruptNumber );\r
\r
- WaitForSingleObject( pvInterruptAcknowledgeEvent, INFINITE );\r
- vPortTrace( "vPortGeneratePseudoInterrupt: Interrupt acknowledged, leaving vPortGeneratePseudoInterrupt()\r\n" );\r
+ /* The simulated interrupt is now held pending, but don't actually process it\r
+ yet if this call is within a critical section. It is possible for this to\r
+ be in a critical section as calls to wait for mutexes are accumulative. */\r
+ if( ulCriticalNesting == 0 )\r
+ {\r
+ /* The event handler needs to know to signal the interrupt acknowledge event\r
+ the next time this task runs. */\r
+ pxThreadState = ( xThreadState * ) *( ( unsigned long * ) pxCurrentTCB );\r
+ SetEvent( pvInterruptEvent ); \r
}\r
+\r
+ ReleaseMutex( pvInterruptEventMutex );\r
}\r
}\r
/*-----------------------------------------------------------*/\r
\r
-void vPortSetInterruptHandler( unsigned long ulInterruptNumber, void (*pvHandler)( void ) )\r
+void vPortSetInterruptHandler( unsigned long ulInterruptNumber, unsigned long (*pvHandler)( void ) )\r
{\r
if( ulInterruptNumber < portMAX_INTERRUPTS )\r
{\r
if( pvInterruptEventMutex != NULL )\r
{\r
WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
- vIsrHandler[ ulInterruptNumber ] = pvHandler;\r
+ ulIsrHandler[ ulInterruptNumber ] = pvHandler;\r
ReleaseMutex( pvInterruptEventMutex );\r
}\r
else\r
{\r
- vIsrHandler[ ulInterruptNumber ] = pvHandler;\r
+ ulIsrHandler[ ulInterruptNumber ] = pvHandler;\r
}\r
}\r
}\r
\r
void vPortEnterCritical( void )\r
{\r
- ulCriticalNesting++;\r
+ if( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED )\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
/*-----------------------------------------------------------*/\r
\r
void vPortExitCritical( void )\r
{\r
+xThreadState *pxThreadState;\r
+long 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
+\r
+ lMutexNeedsReleasing = pdTRUE;\r
+\r
if( ulCriticalNesting > portNO_CRITICAL_NESTING )\r
{\r
- ulCriticalNesting--;\r
-\r
- if( ulCriticalNesting == 0 )\r
+ if( ulCriticalNesting == ( portNO_CRITICAL_NESTING + 1 ) )\r
{\r
+ ulCriticalNesting--;\r
+\r
/* Were any interrupts set to pending while interrupts were \r
- (pseudo) disabled? */\r
+ (simulated) disabled? */\r
if( ulPendingInterrupts != 0UL )\r
{\r
- WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
- vPortTrace( "vPortExitCritical: Setting interrupt event\r\n" );\r
SetEvent( pvInterruptEvent );\r
- ReleaseMutex( pvInterruptEventMutex );\r
\r
- vPortTrace( "vPortExitCritical: Waiting interrupt ack\r\n" );\r
- WaitForSingleObject( pvInterruptAcknowledgeEvent, INFINITE );\r
- vPortTrace( "vPortExitCritical: Interrupt acknowledged, leaving critical section code\r\n" );\r
+ /* The event handler needs to know to signal the interrupt \r
+ acknowledge event the next time this task runs. */\r
+ pxThreadState = ( xThreadState * ) *( ( unsigned long * ) pxCurrentTCB );\r
\r
- /* Just in case the Yield does not happen immediately. This\r
- line could be dangerious if not all interrupts are being\r
- processed. */\r
-// while( ulPendingInterrupts != 0UL );\r
+ /* Mutex will be released now, so does not require releasing\r
+ on function exit. */\r
+ lMutexNeedsReleasing = pdFALSE;\r
+ ReleaseMutex( pvInterruptEventMutex );\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( lMutexNeedsReleasing == pdTRUE )\r
+ {\r
+ ReleaseMutex( pvInterruptEventMutex );\r
}\r
}\r
+/*-----------------------------------------------------------*/\r
+\r