section while pseudo interrupts were pending. */\r
long lWaitingInterruptAck; \r
\r
- /* Critical nesting count of the task - each task has its own. */\r
- portSTACK_TYPE ulCriticalNesting;\r
-\r
/* Handle of the thread that executes the task. */\r
void * pvThread; \r
} xThreadState;\r
vPortTrace( "prvSimulatedPeripheralTimer: Sleep expired, waiting interrupt event mutex\r\n" );\r
WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
vPortTrace( "prvSimulatedPeripheralTimer: Got interrupt event mutex\r\n" );\r
- \r
- /* The timer has expired, generate the simulated tick event. */\r
- ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );\r
-\r
- /* The interrupt is now pending - but should only be processed if\r
- interrupts are actually enabled. */\r
- if( ulCriticalNesting == 0UL )\r
- {\r
- vPortTrace( "prvSimulatedPeripheralTimer: Setting interrupt event to signal tick\r\n" );\r
- SetEvent( pvInterruptEvent );\r
\r
- /* Give back the mutex so the pseudo interrupt handler unblocks \r
- and can access the interrupt handler variables. This high priority\r
- task will then loop back round after waiting for the lower priority \r
- pseudo interrupt handler thread to acknowledge the tick. */\r
- vPortTrace( "prvSimulatedPeripheralTimer: Releasing interrupt event mutex so tick can be processed\r\n" );\r
- SignalObjectAndWait( pvInterruptEventMutex, pvTickAcknowledgeEvent, INFINITE, FALSE );\r
- }\r
- else\r
+ /* A thread will hold the interrupt event mutex while in a critical\r
+ section, so ulCriticalSection should be zero for this tick event to be\r
+ possible. */\r
+ if( ulCriticalNesting != 0 )\r
{\r
- ReleaseMutex( pvInterruptEventMutex );\r
+ /* For a break point only. */\r
+ __asm{ NOP };\r
}\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
+ vPortTrace( "prvSimulatedPeripheralTimer: Setting interrupt event to signal tick\r\n" );\r
+ SetEvent( pvInterruptEvent );\r
+\r
+ /* Give back the mutex so the pseudo interrupt handler unblocks \r
+ and can access the interrupt handler variables. This high priority\r
+ task will then loop back round after waiting for the lower priority \r
+ pseudo interrupt handler thread to acknowledge the tick. */\r
+ vPortTrace( "prvSimulatedPeripheralTimer: Releasing interrupt event mutex so tick can be processed\r\n" );\r
+ SignalObjectAndWait( pvInterruptEventMutex, pvTickAcknowledgeEvent, INFINITE, FALSE );\r
}\r
}\r
/*-----------------------------------------------------------*/\r
/* Create the thread itself. */\r
pxThreadState->pvThread = ( void * ) CreateThread( NULL, 0, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED, NULL );\r
SetThreadPriorityBoost( pxThreadState->pvThread, TRUE );\r
- pxThreadState->ulCriticalNesting = portNO_CRITICAL_NESTING;\r
pxThreadState->lWaitingInterruptAck = pdFALSE;\r
SetThreadPriority( pxThreadState->pvThread, THREAD_PRIORITY_IDLE );\r
\r
WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, INFINITE );\r
vPortTrace( "prvProcessPseudoInterrupts: Got interrupt event and mutex\r\n" );\r
\r
+ /* A thread will hold the interrupt event mutex while in a critical\r
+ section, so this pseudo interrupt handler should only run when\r
+ critical nesting is zero. */\r
+ if( ulCriticalNesting != 0 )\r
+ {\r
+ /* For a break point only. */\r
+ __asm{ NOP };\r
+ }\r
+\r
/* Used to indicate whether the pseudo interrupt processing has\r
necessitated a context switch to another task/thread. */\r
lSwitchRequired = pdFALSE;\r
set up to process yields while within a critical \r
section. */\r
vPortTrace( "prvProcessPseudoInterrupts: Processing tick event\r\n" );\r
- if( ulCriticalNesting == 0UL )\r
+ /* Process the tick itself. */\r
+ vPortTrace( "prvProcessPseudoInterrupts: Incrementing tick\r\n" );\r
+ vTaskIncrementTick();\r
+ #if( configUSE_PREEMPTION != 0 )\r
{\r
- /* Process the tick itself. */\r
- vPortTrace( "prvProcessPseudoInterrupts: Incrementing tick\r\n" );\r
- vTaskIncrementTick();\r
- #if( configUSE_PREEMPTION != 0 )\r
- {\r
- /* A context switch is only automatically \r
- performed from the tick interrupt if the \r
- pre-emptive scheduler is being used. */\r
- lSwitchRequired = pdTRUE;\r
- }\r
- #endif\r
+ /* A context switch is only automatically \r
+ performed from the tick interrupt if the \r
+ pre-emptive scheduler is being used. */\r
+ lSwitchRequired = pdTRUE;\r
+ }\r
+ #endif\r
\r
- /* Clear the interrupt pending bit. */\r
- ulPendingInterrupts &= ~( 1UL << portINTERRUPT_TICK );\r
+ /* Clear the interrupt pending bit. */\r
+ ulPendingInterrupts &= ~( 1UL << portINTERRUPT_TICK );\r
\r
- vPortTrace( "prvProcessPseudoInterrupts: Acking tick\r\n" );\r
- SetEvent( pvTickAcknowledgeEvent );\r
- }\r
- else\r
- {\r
- /* The tick is held pending in ulCriticalNesting\r
- until such time that pseudo interrupts are enabled\r
- again. */\r
- }\r
+ vPortTrace( "prvProcessPseudoInterrupts: Acking tick\r\n" );\r
+ SetEvent( pvTickAcknowledgeEvent );\r
break;\r
\r
default:\r
\r
- if( ulCriticalNesting == 0UL )\r
+ /* Is a handler installed? */\r
+ if( vIsrHandler[ i ] != NULL )\r
{\r
- /* Is a handler installed? */\r
- if( vIsrHandler[ i ] != NULL )\r
- {\r
- lSwitchRequired = pdTRUE;\r
+ lSwitchRequired = pdTRUE;\r
\r
- /* Run the actual handler. */\r
- vIsrHandler[ i ]();\r
+ /* Run the actual handler. */\r
+ vIsrHandler[ i ]();\r
\r
- /* Clear the interrupt pending bit. */\r
- ulPendingInterrupts &= ~( 1UL << i );\r
+ /* Clear the interrupt pending bit. */\r
+ ulPendingInterrupts &= ~( 1UL << i );\r
\r
- /* TODO: Need to have some sort of handshake \r
- event here for non-tick and none yield \r
- interrupts. */\r
- }\r
+ /* TODO: Need to have some sort of handshake \r
+ event here for non-tick and none yield \r
+ interrupts. */\r
}\r
break;\r
}\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
if( pvOldCurrentTCB != pxCurrentTCB )\r
{\r
/* Suspend the old thread. */\r
+ pxThreadState = ( xThreadState *) *( ( unsigned long * ) pvOldCurrentTCB );\r
SetThreadPriority( pxThreadState->pvThread, THREAD_PRIORITY_IDLE );\r
+ SetThreadPriorityBoost( pxThreadState->pvThread, TRUE );\r
SuspendThread( pxThreadState->pvThread );\r
+ \r
+\r
+\r
+ /* NOTE! - Here lies a problem when the preemptive scheduler is \r
+ used. It would seem Win32 threads do not stop as soon as a\r
+ call to suspend them is made. The co-operative scheduler gets\r
+ around this by having the thread block on a semaphore \r
+ immediately after yielding so it cannot execute any more task\r
+ code until it is once again scheduled to run. This cannot be\r
+ done if the task is pre-empted though, and I have not found an\r
+ equivalent work around for the preemptive situation. */\r
+ \r
+\r
\r
//sprintf( cTraceBuffer, "Event processor: suspending %s, resuming %s\r\n", ((xTCB*)pvOldCurrentTCB)->pcTaskName, ((xTCB*)pxCurrentTCB)->pcTaskName );\r
//vPortTrace( cTraceBuffer );\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
+\r
+ /* Boost the priority of the thread selected to run a little \r
+ in an attempt to get the Windows thread scheduler to act a \r
+ little more like an embedded engineer might expect. */\r
SetThreadPriority( pxThreadState->pvThread, THREAD_PRIORITY_ABOVE_NORMAL );\r
+ SetThreadPriorityBoost( pxThreadState->pvThread, TRUE );\r
ResumeThread( pxThreadState->pvThread );\r
-\r
- if( pxThreadState->lWaitingInterruptAck == pdTRUE )\r
- {\r
- pxThreadState->lWaitingInterruptAck = pdFALSE;\r
- vPortTrace( "prvProcessPseudoInterrupts: Acking interrupt\r\n" );\r
- SetEvent( pvInterruptAcknowledgeEvent );\r
- }\r
}\r
}\r
- else\r
+\r
+ /* On exiting a critical section a task may have blocked on the\r
+ interrupt event when only a tick needed processing, in which case\r
+ it will not have been released from waiting on the event yet. */\r
+ pxThreadState = ( xThreadState * ) ( *( unsigned long *) pxCurrentTCB );\r
+ if( pxThreadState->lWaitingInterruptAck == pdTRUE )\r
{\r
- /* On exiting a critical section a task may have blocked on the\r
- interrupt event when only a tick needed processing, in which case\r
- it will not have been released from waiting on the event yet. */\r
- pxThreadState = ( xThreadState * ) ( *( unsigned long *) pxCurrentTCB );\r
- if( pxThreadState->lWaitingInterruptAck == pdTRUE )\r
- {\r
- pxThreadState->lWaitingInterruptAck = pdFALSE;\r
- vPortTrace( "prvProcessPseudoInterrupts: Acking interrupt even though a yield has not been performed.\r\n" );\r
- SetEvent( pvInterruptAcknowledgeEvent );\r
- }\r
+ pxThreadState->lWaitingInterruptAck = pdFALSE;\r
+ vPortTrace( "prvProcessPseudoInterrupts: Acking interrupt even though a yield has not been performed.\r\n" );\r
+ SetEvent( pvInterruptAcknowledgeEvent );\r
}\r
\r
ReleaseMutex( pvInterruptEventMutex );\r
WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
ulPendingInterrupts |= ( 1 << ulInterruptNumber );\r
\r
- if( ulCriticalNesting == 0 ) //|| ( ulInterruptNumber == portINTERRUPT_YIELD ) )\r
+ /* The pseudo 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
}\r
\r
SignalObjectAndWait( pvInterruptEventMutex, pvInterruptAcknowledgeEvent, INFINITE, FALSE );\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 interrupt ack\r\n" );\r
-\r
-// WaitForSingleObject( pvInterruptAcknowledgeEvent, INFINITE );\r
- vPortTrace( "vPortGeneratePseudoInterrupt: Interrupt acknowledged, leaving vPortGeneratePseudoInterrupt()\r\n" );\r
}\r
else\r
{\r
{\r
if( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED )\r
{\r
+ /* The interrupt event mutex is held for the entire critical section,\r
+ effectively disabling (pseudo) interrupts. */\r
WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
-// SuspendThread( pvSimulatedTimerThread );\r
ulCriticalNesting++;\r
- ReleaseMutex( pvInterruptEventMutex );\r
}\r
else\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
if( ulCriticalNesting == ( portNO_CRITICAL_NESTING + 1 ) )\r
{\r
- /* Wait for the interrupt event mutex prior to manipulating or \r
- testing the pseudo interrupt control variables. */\r
- WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
- vPortTrace( "vPortExitCritical: Got interrupt event mutex\r\n" );\r
-\r
-// ResumeThread( pvSimulatedTimerThread );\r
-\r
- /* Now it is safe to decrement the critical nesting count as no\r
- tick events will be processed until the interrupt event mutex is\r
- given back. */\r
ulCriticalNesting--;\r
\r
/* Were any interrupts set to pending while interrupts were \r
{\r
SetEvent( pvInterruptEvent );\r
\r
- /* The interrupt ack event should not be signaled yet - if it \r
- is then there is an error in the logical simulation. */\r
- if( WaitForSingleObject( pvInterruptAcknowledgeEvent, 0 ) != WAIT_TIMEOUT )\r
- {\r
- /* This line is for a break point only. */\r
- __asm { NOP };\r
- }\r
-\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
pxThreadState->lWaitingInterruptAck = pdTRUE;\r
\r
+ /* Mutex will be released now, so does not require releasing\r
+ on function exit. */\r
+ lMutexNeedsReleasing = pdFALSE;\r
SignalObjectAndWait( pvInterruptEventMutex, pvInterruptAcknowledgeEvent, INFINITE, FALSE );\r
- /* Give back the interrupt event mutex so the event can be processed. */\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
}\r
- else\r
- {\r
- /* Can't leave here without giving back the interrupt event\r
- mutex. */\r
- ReleaseMutex( pvInterruptEventMutex );\r
- }\r
}\r
else\r
{\r
ulCriticalNesting--;\r
}\r
}\r
+\r
+ if( lMutexNeedsReleasing == pdTRUE )\r
+ {\r
+ ReleaseMutex( pvInterruptEventMutex );\r
+ }\r
}\r