2 * FreeRTOS Kernel <DEVELOPMENT BRANCH>
3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 * SPDX-License-Identifier: MIT
7 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8 * this software and associated documentation files (the "Software"), to deal in
9 * the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 * the Software, and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 * https://www.FreeRTOS.org
25 * https://github.com/FreeRTOS
29 /* Standard includes. */
32 /* Scheduler includes. */
39 #pragma comment(lib, "winmm.lib")
42 #define portMAX_INTERRUPTS ( ( uint32_t ) sizeof( uint32_t ) * 8UL ) /* The number of bits in an uint32_t. */
43 #define portNO_CRITICAL_NESTING ( ( uint32_t ) 0 )
45 /* The priorities at which the various components of the simulation execute. */
46 #define portDELETE_SELF_THREAD_PRIORITY THREAD_PRIORITY_TIME_CRITICAL /* Must be highest. */
47 #define portSIMULATED_INTERRUPTS_THREAD_PRIORITY THREAD_PRIORITY_TIME_CRITICAL
48 #define portSIMULATED_TIMER_THREAD_PRIORITY THREAD_PRIORITY_HIGHEST
49 #define portTASK_THREAD_PRIORITY THREAD_PRIORITY_ABOVE_NORMAL
52 * Created as a high priority thread, this function uses a timer to simulate
53 * a tick interrupt being generated on an embedded target. In this Windows
54 * environment the timer does not achieve anything approaching real time
57 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter );
60 * Process all the simulated interrupts - each represented by a bit in
61 * ulPendingInterrupts variable.
63 static void prvProcessSimulatedInterrupts( void );
66 * Interrupt handlers used by the kernel itself. These are executed from the
67 * simulated interrupt handler thread.
69 static uint32_t prvProcessYieldInterrupt( void );
70 static uint32_t prvProcessTickInterrupt( void );
73 * Exiting a critical section will cause the calling task to block on yield
74 * event to wait for an interrupt to process if an interrupt was pended while
75 * inside the critical section. This variable protects against a recursive
76 * attempt to obtain pvInterruptEventMutex if a critical section is used inside
77 * an interrupt handler itself.
79 volatile BaseType_t xInsideInterrupt = pdFALSE;
82 * Called when the process exits to let Windows know the high timer resolution
83 * is no longer required.
85 static BOOL WINAPI prvEndProcess( DWORD dwCtrlType );
87 /*-----------------------------------------------------------*/
89 /* The WIN32 simulator runs each task in a thread. The context switching is
90 * managed by the threads, so the task stack does not have to be managed directly,
91 * although the task stack is still used to hold an xThreadState structure this is
92 * the only thing it will ever hold. The structure indirectly maps the task handle
93 * to a thread handle. */
96 /* Handle of the thread that executes the task. */
99 /* Event used to make sure the thread does not execute past a yield point
100 * between the call to SuspendThread() to suspend the thread and the
101 * asynchronous SuspendThread() operation actually being performed. */
105 /* Simulated interrupts waiting to be processed. This is a bit mask where each
106 * bit represents one interrupt, so a maximum of 32 interrupts can be simulated. */
107 static volatile uint32_t ulPendingInterrupts = 0UL;
109 /* An event used to inform the simulated interrupt processing thread (a high
110 * priority thread that simulated interrupt processing) that an interrupt is
112 static void * pvInterruptEvent = NULL;
114 /* Mutex used to protect all the simulated interrupt variables that are accessed
115 * by multiple threads. */
116 static void * pvInterruptEventMutex = NULL;
118 /* The critical nesting count for the currently executing task. This is
119 * initialised to a non-zero value so interrupts do not become enabled during
120 * the initialisation phase. As each task has its own critical nesting value
121 * ulCriticalNesting will get set to zero when the first task runs. This
122 * initialisation is probably not critical in this simulated environment as the
123 * simulated interrupt handlers do not get created until the FreeRTOS scheduler is
125 static volatile uint32_t ulCriticalNesting = 9999UL;
127 /* Handlers for all the simulated software interrupts. The first two positions
128 * are used for the Yield and Tick interrupts so are handled slightly differently,
129 * all the other interrupts can be user defined. */
130 static uint32_t (* ulIsrHandler[ portMAX_INTERRUPTS ])( void ) = { 0 };
132 /* Pointer to the TCB of the currently executing task. */
133 extern void * volatile pxCurrentTCB;
135 /* Used to ensure nothing is processed during the startup sequence. */
136 static BaseType_t xPortRunning = pdFALSE;
138 /*-----------------------------------------------------------*/
140 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter )
142 TickType_t xMinimumWindowsBlockTime;
144 TickType_t xWaitTimeBetweenTicks = portTICK_PERIOD_MS;
145 HANDLE hTimer = NULL;
146 LARGE_INTEGER liDueTime;
148 /* Set the timer resolution to the maximum possible. */
149 if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )
151 xMinimumWindowsBlockTime = ( TickType_t ) xTimeCaps.wPeriodMin;
152 timeBeginPeriod( xTimeCaps.wPeriodMin );
154 /* Register an exit handler so the timeBeginPeriod() function can be
155 * matched with a timeEndPeriod() when the application exits. */
156 SetConsoleCtrlHandler( prvEndProcess, TRUE );
160 xMinimumWindowsBlockTime = ( TickType_t ) 20;
163 /* Just to prevent compiler warnings. */
164 ( void ) lpParameter;
166 /* Tick time for the timer is adjusted with the maximum available
168 if( portTICK_PERIOD_MS < xMinimumWindowsBlockTime )
170 xWaitTimeBetweenTicks = xMinimumWindowsBlockTime;
173 /* Convert the tick time in milliseconds to nanoseconds resolution
174 for the Waitable Timer. */
175 liDueTime.u.LowPart = xWaitTimeBetweenTicks * 1000 * 1000;
176 liDueTime.u.HighPart = 0;
178 /* Create a synchronization Waitable Timer.*/
179 hTimer = CreateWaitableTimer( NULL, FALSE, NULL );
181 configASSERT( hTimer != NULL );
183 /* Set the Waitable Timer. The timer is set to run periodically at every
184 xWaitTimeBetweenTicks milliseconds. */
185 configASSERT( SetWaitableTimer( hTimer, &liDueTime, xWaitTimeBetweenTicks, NULL, NULL, 0 ) );
187 while( xPortRunning == pdTRUE )
189 /* Wait until the timer expires and we can access the simulated interrupt
192 WaitForSingleObject( hTimer, INFINITE );
194 vPortGenerateSimulatedInterruptFromWindowsThread( portINTERRUPT_TICK );
199 /*-----------------------------------------------------------*/
201 static BOOL WINAPI prvEndProcess( DWORD dwCtrlType )
207 if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )
209 /* Match the call to timeBeginPeriod( xTimeCaps.wPeriodMin ) made when
210 * the process started with a timeEndPeriod() as the process exits. */
211 timeEndPeriod( xTimeCaps.wPeriodMin );
216 /*-----------------------------------------------------------*/
218 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
219 TaskFunction_t pxCode,
220 void * pvParameters )
222 ThreadState_t * pxThreadState = NULL;
223 int8_t * pcTopOfStack = ( int8_t * ) pxTopOfStack;
224 const SIZE_T xStackSize = 1024; /* Set the size to a small number which will get rounded up to the minimum possible. */
226 /* In this simulated case a stack is not initialised, but instead a thread
227 * is created that will execute the task being created. The thread handles
228 * the context switching itself. The ThreadState_t object is placed onto
229 * the stack that was created for the task - so the stack buffer is still
230 * used, just not in the conventional way. It will not be used for anything
231 * other than holding this structure. */
232 pxThreadState = ( ThreadState_t * ) ( pcTopOfStack - sizeof( ThreadState_t ) );
234 /* Create the event used to prevent the thread from executing past its yield
235 * point if the SuspendThread() call that suspends the thread does not take
236 * effect immediately (it is an asynchronous call). */
237 pxThreadState->pvYieldEvent = CreateEvent( NULL, /* Default security attributes. */
238 FALSE, /* Auto reset. */
239 FALSE, /* Start not signalled. */
240 NULL ); /* No name. */
244 /* GCC reports the warning for the cast operation from TaskFunction_t to LPTHREAD_START_ROUTINE. */
245 /* Disable this warning here by the #pragma option. */
246 #pragma GCC diagnostic push
247 #pragma GCC diagnostic ignored "-Wcast-function-type"
249 /* Create the thread itself. */
250 pxThreadState->pvThread = CreateThread( NULL, xStackSize, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, NULL );
252 #pragma GCC diagnostic pop
255 configASSERT( pxThreadState->pvThread ); /* See comment where TerminateThread() is called. */
256 SetThreadAffinityMask( pxThreadState->pvThread, 0x01 );
257 SetThreadPriorityBoost( pxThreadState->pvThread, TRUE );
258 SetThreadPriority( pxThreadState->pvThread, portTASK_THREAD_PRIORITY );
260 return ( StackType_t * ) pxThreadState;
262 /*-----------------------------------------------------------*/
264 BaseType_t xPortStartScheduler( void )
266 void * pvHandle = NULL;
268 ThreadState_t * pxThreadState = NULL;
269 SYSTEM_INFO xSystemInfo;
271 /* This port runs windows threads with extremely high priority. All the
272 * threads execute on the same core - to prevent locking up the host only start
273 * if the host has multiple cores. */
274 GetSystemInfo( &xSystemInfo );
276 if( xSystemInfo.dwNumberOfProcessors <= 1 )
278 printf( "This version of the FreeRTOS Windows port can only be used on multi-core hosts.\r\n" );
285 /* The highest priority class is used to [try to] prevent other Windows
286 * activity interfering with FreeRTOS timing too much. */
287 if( SetPriorityClass( GetCurrentProcess(), REALTIME_PRIORITY_CLASS ) == 0 )
289 printf( "SetPriorityClass() failed\r\n" );
292 /* Install the interrupt handlers used by the scheduler itself. */
293 vPortSetInterruptHandler( portINTERRUPT_YIELD, prvProcessYieldInterrupt );
294 vPortSetInterruptHandler( portINTERRUPT_TICK, prvProcessTickInterrupt );
296 /* Create the events and mutexes that are used to synchronise all the
298 pvInterruptEventMutex = CreateMutex( NULL, FALSE, NULL );
299 pvInterruptEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
301 if( ( pvInterruptEventMutex == NULL ) || ( pvInterruptEvent == NULL ) )
306 /* Set the priority of this thread such that it is above the priority of
307 * the threads that run tasks. This higher priority is required to ensure
308 * simulated interrupts take priority over tasks. */
309 pvHandle = GetCurrentThread();
311 if( pvHandle == NULL )
317 if( lSuccess == pdPASS )
319 if( SetThreadPriority( pvHandle, portSIMULATED_INTERRUPTS_THREAD_PRIORITY ) == 0 )
324 SetThreadPriorityBoost( pvHandle, TRUE );
325 SetThreadAffinityMask( pvHandle, 0x01 );
328 if( lSuccess == pdPASS )
330 /* Start the thread that simulates the timer peripheral to generate
331 * tick interrupts. The priority is set below that of the simulated
332 * interrupt handler so the interrupt event mutex is used for the
333 * handshake / overrun protection. */
334 pvHandle = CreateThread( NULL, 0, prvSimulatedPeripheralTimer, NULL, CREATE_SUSPENDED, NULL );
336 if( pvHandle != NULL )
338 SetThreadPriority( pvHandle, portSIMULATED_TIMER_THREAD_PRIORITY );
339 SetThreadPriorityBoost( pvHandle, TRUE );
340 SetThreadAffinityMask( pvHandle, 0x01 );
341 ResumeThread( pvHandle );
344 /* Start the highest priority task by obtaining its associated thread
345 * state structure, in which is stored the thread handle. */
346 pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
347 ulCriticalNesting = portNO_CRITICAL_NESTING;
349 /* The scheduler is now running. */
350 xPortRunning = pdTRUE;
352 /* Start the first task. */
353 ResumeThread( pxThreadState->pvThread );
355 /* Handle all simulated interrupts - including yield requests and
356 * simulated ticks. */
357 prvProcessSimulatedInterrupts();
360 /* Would not expect to return from prvProcessSimulatedInterrupts(), so should
364 /*-----------------------------------------------------------*/
366 static uint32_t prvProcessYieldInterrupt( void )
368 /* Always return true as this is a yield. */
371 /*-----------------------------------------------------------*/
373 static uint32_t prvProcessTickInterrupt( void )
375 uint32_t ulSwitchRequired;
377 /* Process the tick itself. */
378 configASSERT( xPortRunning );
379 ulSwitchRequired = ( uint32_t ) xTaskIncrementTick();
381 return ulSwitchRequired;
383 /*-----------------------------------------------------------*/
385 static void prvProcessSimulatedInterrupts( void )
387 uint32_t ulSwitchRequired, i;
388 ThreadState_t * pxThreadState;
389 void * pvObjectList[ 2 ];
392 const DWORD xTimeoutMilliseconds = 1000;
394 /* Going to block on the mutex that ensured exclusive access to the simulated
395 * interrupt objects, and the event that signals that a simulated interrupt
396 * should be processed. */
397 pvObjectList[ 0 ] = pvInterruptEventMutex;
398 pvObjectList[ 1 ] = pvInterruptEvent;
400 /* Create a pending tick to ensure the first task is started as soon as
401 * this thread pends. */
402 ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );
403 SetEvent( pvInterruptEvent );
405 while( xPortRunning == pdTRUE )
407 xInsideInterrupt = pdFALSE;
409 /* Wait with timeout so that we can exit from this loop when
410 * the scheduler is stopped by calling vPortEndScheduler. */
411 xWinApiResult = WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, xTimeoutMilliseconds );
413 if( xWinApiResult != WAIT_TIMEOUT )
415 /* Cannot be in a critical section to get here. Tasks that exit a
416 * critical section will block on a yield mutex to wait for an interrupt to
417 * process if an interrupt was set pending while the task was inside the
418 * critical section. xInsideInterrupt prevents interrupts that contain
419 * critical sections from doing the same. */
420 xInsideInterrupt = pdTRUE;
422 /* Used to indicate whether the simulated interrupt processing has
423 * necessitated a context switch to another task/thread. */
424 ulSwitchRequired = pdFALSE;
426 /* For each interrupt we are interested in processing, each of which is
427 * represented by a bit in the 32bit ulPendingInterrupts variable. */
428 for( i = 0; i < portMAX_INTERRUPTS; i++ )
430 /* Is the simulated interrupt pending? */
431 if( ( ulPendingInterrupts & ( 1UL << i ) ) != 0 )
433 /* Is a handler installed? */
434 if( ulIsrHandler[ i ] != NULL )
436 /* Run the actual handler. Handlers return pdTRUE if they
437 * necessitate a context switch. */
438 if( ulIsrHandler[ i ]() != pdFALSE )
440 /* A bit mask is used purely to help debugging. */
441 ulSwitchRequired |= ( 1 << i );
445 /* Clear the interrupt pending bit. */
446 ulPendingInterrupts &= ~( 1UL << i );
450 if( ulSwitchRequired != pdFALSE )
452 /* Suspend the old thread. */
453 pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
454 SuspendThread( pxThreadState->pvThread );
456 /* Ensure the thread is actually suspended by performing a
457 * synchronous operation that can only complete when the thread
458 * is actually suspended. The below code asks for dummy register
459 * data. Experimentation shows that these two lines don't appear
460 * to do anything now, but according to
461 * https://devblogs.microsoft.com/oldnewthing/20150205-00/?p=44743
462 * they do - so as they do not harm (slight run-time hit). */
463 xContext.ContextFlags = CONTEXT_INTEGER;
464 ( void ) GetThreadContext( pxThreadState->pvThread, &xContext );
466 /* Select the next task to run. */
467 vTaskSwitchContext();
469 /* Obtain the state of the task now selected to enter the
471 pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pxCurrentTCB );
473 /* pxThreadState->pvThread can be NULL if the task deleted
474 * itself - but a deleted task should never be resumed here. */
475 configASSERT( pxThreadState->pvThread != NULL );
476 ResumeThread( pxThreadState->pvThread );
479 /* If the thread that is about to be resumed stopped running
480 * because it yielded then it will wait on an event when it resumed
481 * (to ensure it does not continue running after the call to
482 * SuspendThread() above as SuspendThread() is asynchronous).
483 * Signal the event to ensure the thread can proceed now it is
484 * valid for it to do so. Signaling the event is benign in the case that
485 * the task was switched out asynchronously by an interrupt as the event
486 * is reset before the task blocks on it. */
487 pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pxCurrentTCB );
488 SetEvent( pxThreadState->pvYieldEvent );
489 ReleaseMutex( pvInterruptEventMutex );
493 /*-----------------------------------------------------------*/
495 void vPortDeleteThread( void * pvTaskToDelete )
497 ThreadState_t * pxThreadState;
498 uint32_t ulErrorCode;
500 /* Remove compiler warnings if configASSERT() is not defined. */
501 ( void ) ulErrorCode;
503 /* Find the handle of the thread being deleted. */
504 pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pvTaskToDelete );
506 /* Check that the thread is still valid, it might have been closed by
507 * vPortCloseRunningThread() - which will be the case if the task associated
508 * with the thread originally deleted itself rather than being deleted by a
510 if( pxThreadState->pvThread != NULL )
512 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
514 /* !!! This is not a nice way to terminate a thread, and will eventually
515 * result in resources being depleted if tasks frequently delete other
516 * tasks (rather than deleting themselves) as the task stacks will not be
518 ulErrorCode = TerminateThread( pxThreadState->pvThread, 0 );
519 configASSERT( ulErrorCode );
521 ulErrorCode = CloseHandle( pxThreadState->pvThread );
522 configASSERT( ulErrorCode );
524 ReleaseMutex( pvInterruptEventMutex );
527 /*-----------------------------------------------------------*/
529 void vPortCloseRunningThread( void * pvTaskToDelete,
530 volatile BaseType_t * pxPendYield )
532 ThreadState_t * pxThreadState;
534 uint32_t ulErrorCode;
536 /* Remove compiler warnings if configASSERT() is not defined. */
537 ( void ) ulErrorCode;
539 /* Find the handle of the thread being deleted. */
540 pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pvTaskToDelete );
541 pvThread = pxThreadState->pvThread;
543 /* Raise the Windows priority of the thread to ensure the FreeRTOS scheduler
544 * does not run and swap it out before it is closed. If that were to happen
545 * the thread would never run again and effectively be a thread handle and
547 SetThreadPriority( pvThread, portDELETE_SELF_THREAD_PRIORITY );
549 /* This function will not return, therefore a yield is set as pending to
550 * ensure a context switch occurs away from this thread on the next tick. */
551 *pxPendYield = pdTRUE;
553 /* Mark the thread associated with this task as invalid so
554 * vPortDeleteThread() does not try to terminate it. */
555 pxThreadState->pvThread = NULL;
557 /* Close the thread. */
558 ulErrorCode = CloseHandle( pvThread );
559 configASSERT( ulErrorCode );
561 /* This is called from a critical section, which must be exited before the
565 /* Record that a yield is pending so that the next tick interrupt switches
566 * out this thread regardless of the value of configUSE_PREEMPTION. This is
567 * needed when a task deletes itself - the taskYIELD_WITHIN_API within
568 * vTaskDelete does not get called because this function never returns. If
569 * we do not pend portINTERRUPT_YIELD here, the next task is not scheduled
570 * when configUSE_PREEMPTION is set to 0. */
571 if( pvInterruptEventMutex != NULL )
573 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
574 ulPendingInterrupts |= ( 1 << portINTERRUPT_YIELD );
575 ReleaseMutex( pvInterruptEventMutex );
578 CloseHandle( pxThreadState->pvYieldEvent );
581 /*-----------------------------------------------------------*/
583 void vPortEndScheduler( void )
585 xPortRunning = pdFALSE;
587 /*-----------------------------------------------------------*/
589 void vPortGenerateSimulatedInterrupt( uint32_t ulInterruptNumber )
591 ThreadState_t * pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
593 configASSERT( xPortRunning );
595 if( ( ulInterruptNumber < portMAX_INTERRUPTS ) && ( pvInterruptEventMutex != NULL ) )
597 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
598 ulPendingInterrupts |= ( 1 << ulInterruptNumber );
600 /* The simulated interrupt is now held pending, but don't actually
601 * process it yet if this call is within a critical section. It is
602 * possible for this to be in a critical section as calls to wait for
603 * mutexes are accumulative. If in a critical section then the event
604 * will get set when the critical section nesting count is wound back
606 if( ulCriticalNesting == portNO_CRITICAL_NESTING )
608 SetEvent( pvInterruptEvent );
610 /* Going to wait for an event - make sure the event is not already
612 ResetEvent( pxThreadState->pvYieldEvent );
615 ReleaseMutex( pvInterruptEventMutex );
617 if( ulCriticalNesting == portNO_CRITICAL_NESTING )
619 /* An interrupt was pended so ensure to block to allow it to
620 * execute. In most cases the (simulated) interrupt will have
621 * executed before the next line is reached - so this is just to make
623 WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );
627 /*-----------------------------------------------------------*/
629 void vPortGenerateSimulatedInterruptFromWindowsThread( uint32_t ulInterruptNumber )
631 if( xPortRunning == pdTRUE )
633 /* Can't proceed if in a critical section as pvInterruptEventMutex won't
635 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
637 /* Pending a user defined interrupt to be handled in simulated interrupt
639 ulPendingInterrupts |= ( 1 << ulInterruptNumber );
641 /* The interrupt is now pending - notify the simulated interrupt
642 * handler thread. Must be outside of a critical section to get here so
643 * the handler thread can execute immediately pvInterruptEventMutex is
645 configASSERT( ulCriticalNesting == 0UL );
646 SetEvent( pvInterruptEvent );
648 /* Give back the mutex so the simulated interrupt handler unblocks
649 * and can access the interrupt handler variables. */
650 ReleaseMutex( pvInterruptEventMutex );
653 /*-----------------------------------------------------------*/
655 void vPortSetInterruptHandler( uint32_t ulInterruptNumber,
656 uint32_t ( * pvHandler )( void ) )
658 if( ulInterruptNumber < portMAX_INTERRUPTS )
660 if( pvInterruptEventMutex != NULL )
662 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
663 ulIsrHandler[ ulInterruptNumber ] = pvHandler;
664 ReleaseMutex( pvInterruptEventMutex );
668 ulIsrHandler[ ulInterruptNumber ] = pvHandler;
672 /*-----------------------------------------------------------*/
674 void vPortEnterCritical( void )
676 if( xPortRunning == pdTRUE )
678 /* The interrupt event mutex is held for the entire critical section,
679 * effectively disabling (simulated) interrupts. */
680 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
685 /*-----------------------------------------------------------*/
687 void vPortExitCritical( void )
689 int32_t lMutexNeedsReleasing;
691 /* The interrupt event mutex should already be held by this thread as it was
692 * obtained on entry to the critical section. */
693 lMutexNeedsReleasing = pdTRUE;
695 if( ulCriticalNesting > portNO_CRITICAL_NESTING )
699 /* Don't need to wait for any pending interrupts to execute if the
700 * critical section was exited from inside an interrupt. */
701 if( ( ulCriticalNesting == portNO_CRITICAL_NESTING ) && ( xInsideInterrupt == pdFALSE ) )
703 /* Were any interrupts set to pending while interrupts were
704 * (simulated) disabled? */
705 if( ulPendingInterrupts != 0UL )
707 ThreadState_t * pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
709 configASSERT( xPortRunning );
711 /* The interrupt won't actually executed until
712 * pvInterruptEventMutex is released as it waits on both
713 * pvInterruptEventMutex and pvInterruptEvent.
714 * pvInterruptEvent is only set when the simulated
715 * interrupt is pended if the interrupt is pended
716 * from outside a critical section - hence it is set
718 SetEvent( pvInterruptEvent );
720 /* The calling task is going to wait for an event to ensure the
721 * interrupt that is pending executes immediately after the
722 * critical section is exited - so make sure the event is not
723 * already signaled. */
724 ResetEvent( pxThreadState->pvYieldEvent );
726 /* Mutex will be released now so the (simulated) interrupt can
727 * execute, so does not require releasing on function exit. */
728 lMutexNeedsReleasing = pdFALSE;
729 ReleaseMutex( pvInterruptEventMutex );
730 WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );
735 if( pvInterruptEventMutex != NULL )
737 if( lMutexNeedsReleasing == pdTRUE )
739 configASSERT( xPortRunning );
740 ReleaseMutex( pvInterruptEventMutex );
744 /*-----------------------------------------------------------*/