]> begriffs open source - freertos/blob - portable/MSVC-MingW/port.c
Reinstate "Fix inaccurate ticks in windows port" (#1198)
[freertos] / portable / MSVC-MingW / port.c
1 /*
2  * FreeRTOS Kernel <DEVELOPMENT BRANCH>
3  * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
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.
23  *
24  * https://www.FreeRTOS.org
25  * https://github.com/FreeRTOS
26  *
27  */
28
29 /* Standard includes. */
30 #include <stdio.h>
31
32 /* Scheduler includes. */
33 #include "FreeRTOS.h"
34 #include "task.h"
35
36 #ifdef __GNUC__
37     #include "mmsystem.h"
38 #else
39     #pragma comment(lib, "winmm.lib")
40 #endif
41
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 )
44
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
50
51 /*
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
55  * performance though.
56  */
57 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter );
58
59 /*
60  * Process all the simulated interrupts - each represented by a bit in
61  * ulPendingInterrupts variable.
62  */
63 static void prvProcessSimulatedInterrupts( void );
64
65 /*
66  * Interrupt handlers used by the kernel itself.  These are executed from the
67  * simulated interrupt handler thread.
68  */
69 static uint32_t prvProcessYieldInterrupt( void );
70 static uint32_t prvProcessTickInterrupt( void );
71
72 /*
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.
78  */
79 volatile BaseType_t xInsideInterrupt = pdFALSE;
80
81 /*
82  * Called when the process exits to let Windows know the high timer resolution
83  * is no longer required.
84  */
85 static BOOL WINAPI prvEndProcess( DWORD dwCtrlType );
86
87 /*-----------------------------------------------------------*/
88
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. */
94 typedef struct
95 {
96     /* Handle of the thread that executes the task. */
97     void * pvThread;
98
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. */
102     void * pvYieldEvent;
103 } ThreadState_t;
104
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;
108
109 /* An event used to inform the simulated interrupt processing thread (a high
110  * priority thread that simulated interrupt processing) that an interrupt is
111  * pending. */
112 static void * pvInterruptEvent = NULL;
113
114 /* Mutex used to protect all the simulated interrupt variables that are accessed
115  * by multiple threads. */
116 static void * pvInterruptEventMutex = NULL;
117
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
124  * started anyway. */
125 static volatile uint32_t ulCriticalNesting = 9999UL;
126
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 };
131
132 /* Pointer to the TCB of the currently executing task. */
133 extern void * volatile pxCurrentTCB;
134
135 /* Used to ensure nothing is processed during the startup sequence. */
136 static BaseType_t xPortRunning = pdFALSE;
137
138 /*-----------------------------------------------------------*/
139
140 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter )
141 {
142     TickType_t xMinimumWindowsBlockTime;
143     TIMECAPS xTimeCaps;
144     TickType_t xWaitTimeBetweenTicks = portTICK_PERIOD_MS;
145     HANDLE hTimer = NULL;
146     LARGE_INTEGER liDueTime;
147
148     /* Set the timer resolution to the maximum possible. */
149     if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )
150     {
151         xMinimumWindowsBlockTime = ( TickType_t ) xTimeCaps.wPeriodMin;
152         timeBeginPeriod( xTimeCaps.wPeriodMin );
153
154         /* Register an exit handler so the timeBeginPeriod() function can be
155          * matched with a timeEndPeriod() when the application exits. */
156         SetConsoleCtrlHandler( prvEndProcess, TRUE );
157     }
158     else
159     {
160         xMinimumWindowsBlockTime = ( TickType_t ) 20;
161     }
162
163     /* Just to prevent compiler warnings. */
164     ( void ) lpParameter;
165
166     /* Tick time for the timer is adjusted with the maximum available
167      resolution. */
168     if( portTICK_PERIOD_MS < xMinimumWindowsBlockTime )
169     {
170         xWaitTimeBetweenTicks = xMinimumWindowsBlockTime;
171     }
172
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;
177
178     /* Create a synchronization Waitable Timer.*/
179     hTimer = CreateWaitableTimer( NULL, FALSE, NULL );
180
181     configASSERT( hTimer != NULL );
182
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 ) );
186
187     while( xPortRunning == pdTRUE )
188     {
189         /* Wait until the timer expires and we can access the simulated interrupt
190          * variables. */
191
192         WaitForSingleObject( hTimer, INFINITE );
193
194         vPortGenerateSimulatedInterruptFromWindowsThread( portINTERRUPT_TICK );
195     }
196
197     return 0;
198 }
199 /*-----------------------------------------------------------*/
200
201 static BOOL WINAPI prvEndProcess( DWORD dwCtrlType )
202 {
203     TIMECAPS xTimeCaps;
204
205     ( void ) dwCtrlType;
206
207     if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )
208     {
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 );
212     }
213
214     return pdFALSE;
215 }
216 /*-----------------------------------------------------------*/
217
218 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
219                                      TaskFunction_t pxCode,
220                                      void * pvParameters )
221 {
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. */
225
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 ) );
233
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. */
241
242
243 #ifdef __GNUC__
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"
248 #endif
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 );
251 #ifdef __GNUC__
252 #pragma GCC diagnostic pop
253 #endif
254
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 );
259
260     return ( StackType_t * ) pxThreadState;
261 }
262 /*-----------------------------------------------------------*/
263
264 BaseType_t xPortStartScheduler( void )
265 {
266     void * pvHandle = NULL;
267     int32_t lSuccess;
268     ThreadState_t * pxThreadState = NULL;
269     SYSTEM_INFO xSystemInfo;
270
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 );
275
276     if( xSystemInfo.dwNumberOfProcessors <= 1 )
277     {
278         printf( "This version of the FreeRTOS Windows port can only be used on multi-core hosts.\r\n" );
279         lSuccess = pdFAIL;
280     }
281     else
282     {
283         lSuccess = pdPASS;
284
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 )
288         {
289             printf( "SetPriorityClass() failed\r\n" );
290         }
291
292         /* Install the interrupt handlers used by the scheduler itself. */
293         vPortSetInterruptHandler( portINTERRUPT_YIELD, prvProcessYieldInterrupt );
294         vPortSetInterruptHandler( portINTERRUPT_TICK, prvProcessTickInterrupt );
295
296         /* Create the events and mutexes that are used to synchronise all the
297          * threads. */
298         pvInterruptEventMutex = CreateMutex( NULL, FALSE, NULL );
299         pvInterruptEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
300
301         if( ( pvInterruptEventMutex == NULL ) || ( pvInterruptEvent == NULL ) )
302         {
303             lSuccess = pdFAIL;
304         }
305
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();
310
311         if( pvHandle == NULL )
312         {
313             lSuccess = pdFAIL;
314         }
315     }
316
317     if( lSuccess == pdPASS )
318     {
319         if( SetThreadPriority( pvHandle, portSIMULATED_INTERRUPTS_THREAD_PRIORITY ) == 0 )
320         {
321             lSuccess = pdFAIL;
322         }
323
324         SetThreadPriorityBoost( pvHandle, TRUE );
325         SetThreadAffinityMask( pvHandle, 0x01 );
326     }
327
328     if( lSuccess == pdPASS )
329     {
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 );
335
336         if( pvHandle != NULL )
337         {
338             SetThreadPriority( pvHandle, portSIMULATED_TIMER_THREAD_PRIORITY );
339             SetThreadPriorityBoost( pvHandle, TRUE );
340             SetThreadAffinityMask( pvHandle, 0x01 );
341             ResumeThread( pvHandle );
342         }
343
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;
348
349         /* The scheduler is now running. */
350         xPortRunning = pdTRUE;
351
352         /* Start the first task. */
353         ResumeThread( pxThreadState->pvThread );
354
355         /* Handle all simulated interrupts - including yield requests and
356          * simulated ticks. */
357         prvProcessSimulatedInterrupts();
358     }
359
360     /* Would not expect to return from prvProcessSimulatedInterrupts(), so should
361      * not get here. */
362     return 0;
363 }
364 /*-----------------------------------------------------------*/
365
366 static uint32_t prvProcessYieldInterrupt( void )
367 {
368     /* Always return true as this is a yield. */
369     return pdTRUE;
370 }
371 /*-----------------------------------------------------------*/
372
373 static uint32_t prvProcessTickInterrupt( void )
374 {
375     uint32_t ulSwitchRequired;
376
377     /* Process the tick itself. */
378     configASSERT( xPortRunning );
379     ulSwitchRequired = ( uint32_t ) xTaskIncrementTick();
380
381     return ulSwitchRequired;
382 }
383 /*-----------------------------------------------------------*/
384
385 static void prvProcessSimulatedInterrupts( void )
386 {
387     uint32_t ulSwitchRequired, i;
388     ThreadState_t * pxThreadState;
389     void * pvObjectList[ 2 ];
390     CONTEXT xContext;
391     DWORD xWinApiResult;
392     const DWORD xTimeoutMilliseconds = 1000;
393
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;
399
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 );
404
405     while( xPortRunning == pdTRUE )
406     {
407         xInsideInterrupt = pdFALSE;
408
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 );
412
413         if( xWinApiResult != WAIT_TIMEOUT )
414         {
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;
421
422             /* Used to indicate whether the simulated interrupt processing has
423              * necessitated a context switch to another task/thread. */
424             ulSwitchRequired = pdFALSE;
425
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++ )
429             {
430                 /* Is the simulated interrupt pending? */
431                 if( ( ulPendingInterrupts & ( 1UL << i ) ) != 0 )
432                 {
433                     /* Is a handler installed? */
434                     if( ulIsrHandler[ i ] != NULL )
435                     {
436                         /* Run the actual handler.  Handlers return pdTRUE if they
437                          * necessitate a context switch. */
438                         if( ulIsrHandler[ i ]() != pdFALSE )
439                         {
440                             /* A bit mask is used purely to help debugging. */
441                             ulSwitchRequired |= ( 1 << i );
442                         }
443                     }
444
445                     /* Clear the interrupt pending bit. */
446                     ulPendingInterrupts &= ~( 1UL << i );
447                 }
448             }
449
450             if( ulSwitchRequired != pdFALSE )
451             {
452                 /* Suspend the old thread. */
453                 pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
454                 SuspendThread( pxThreadState->pvThread );
455
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 );
465
466                 /* Select the next task to run. */
467                 vTaskSwitchContext();
468
469                 /* Obtain the state of the task now selected to enter the
470                  * Running state. */
471                 pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pxCurrentTCB );
472
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 );
477             }
478
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 );
490         }
491     }
492 }
493 /*-----------------------------------------------------------*/
494
495 void vPortDeleteThread( void * pvTaskToDelete )
496 {
497     ThreadState_t * pxThreadState;
498     uint32_t ulErrorCode;
499
500     /* Remove compiler warnings if configASSERT() is not defined. */
501     ( void ) ulErrorCode;
502
503     /* Find the handle of the thread being deleted. */
504     pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pvTaskToDelete );
505
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
509      * different task. */
510     if( pxThreadState->pvThread != NULL )
511     {
512         WaitForSingleObject( pvInterruptEventMutex, INFINITE );
513
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
517          * freed. */
518         ulErrorCode = TerminateThread( pxThreadState->pvThread, 0 );
519         configASSERT( ulErrorCode );
520
521         ulErrorCode = CloseHandle( pxThreadState->pvThread );
522         configASSERT( ulErrorCode );
523
524         ReleaseMutex( pvInterruptEventMutex );
525     }
526 }
527 /*-----------------------------------------------------------*/
528
529 void vPortCloseRunningThread( void * pvTaskToDelete,
530                               volatile BaseType_t * pxPendYield )
531 {
532     ThreadState_t * pxThreadState;
533     void * pvThread;
534     uint32_t ulErrorCode;
535
536     /* Remove compiler warnings if configASSERT() is not defined. */
537     ( void ) ulErrorCode;
538
539     /* Find the handle of the thread being deleted. */
540     pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pvTaskToDelete );
541     pvThread = pxThreadState->pvThread;
542
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
546      * memory leak. */
547     SetThreadPriority( pvThread, portDELETE_SELF_THREAD_PRIORITY );
548
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;
552
553     /* Mark the thread associated with this task as invalid so
554      * vPortDeleteThread() does not try to terminate it. */
555     pxThreadState->pvThread = NULL;
556
557     /* Close the thread. */
558     ulErrorCode = CloseHandle( pvThread );
559     configASSERT( ulErrorCode );
560
561     /* This is called from a critical section, which must be exited before the
562      * thread stops. */
563     taskEXIT_CRITICAL();
564
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 )
572     {
573         WaitForSingleObject( pvInterruptEventMutex, INFINITE );
574         ulPendingInterrupts |= ( 1 << portINTERRUPT_YIELD );
575         ReleaseMutex( pvInterruptEventMutex );
576     }
577
578     CloseHandle( pxThreadState->pvYieldEvent );
579     ExitThread( 0 );
580 }
581 /*-----------------------------------------------------------*/
582
583 void vPortEndScheduler( void )
584 {
585     xPortRunning = pdFALSE;
586 }
587 /*-----------------------------------------------------------*/
588
589 void vPortGenerateSimulatedInterrupt( uint32_t ulInterruptNumber )
590 {
591     ThreadState_t * pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
592
593     configASSERT( xPortRunning );
594
595     if( ( ulInterruptNumber < portMAX_INTERRUPTS ) && ( pvInterruptEventMutex != NULL ) )
596     {
597         WaitForSingleObject( pvInterruptEventMutex, INFINITE );
598         ulPendingInterrupts |= ( 1 << ulInterruptNumber );
599
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
605          * down to zero. */
606         if( ulCriticalNesting == portNO_CRITICAL_NESTING )
607         {
608             SetEvent( pvInterruptEvent );
609
610             /* Going to wait for an event - make sure the event is not already
611              * signaled. */
612             ResetEvent( pxThreadState->pvYieldEvent );
613         }
614
615         ReleaseMutex( pvInterruptEventMutex );
616
617         if( ulCriticalNesting == portNO_CRITICAL_NESTING )
618         {
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
622              * sure. */
623             WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );
624         }
625     }
626 }
627 /*-----------------------------------------------------------*/
628
629 void vPortGenerateSimulatedInterruptFromWindowsThread( uint32_t ulInterruptNumber )
630 {
631     if( xPortRunning == pdTRUE )
632     {
633         /* Can't proceed if in a critical section as pvInterruptEventMutex won't
634          * be available. */
635         WaitForSingleObject( pvInterruptEventMutex, INFINITE );
636
637         /* Pending a user defined interrupt to be handled in simulated interrupt
638          * handler thread. */
639         ulPendingInterrupts |= ( 1 << ulInterruptNumber );
640
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
644          * released. */
645         configASSERT( ulCriticalNesting == 0UL );
646         SetEvent( pvInterruptEvent );
647
648         /* Give back the mutex so the simulated interrupt handler unblocks
649          * and can access the interrupt handler variables. */
650         ReleaseMutex( pvInterruptEventMutex );
651     }
652 }
653 /*-----------------------------------------------------------*/
654
655 void vPortSetInterruptHandler( uint32_t ulInterruptNumber,
656                                uint32_t ( * pvHandler )( void ) )
657 {
658     if( ulInterruptNumber < portMAX_INTERRUPTS )
659     {
660         if( pvInterruptEventMutex != NULL )
661         {
662             WaitForSingleObject( pvInterruptEventMutex, INFINITE );
663             ulIsrHandler[ ulInterruptNumber ] = pvHandler;
664             ReleaseMutex( pvInterruptEventMutex );
665         }
666         else
667         {
668             ulIsrHandler[ ulInterruptNumber ] = pvHandler;
669         }
670     }
671 }
672 /*-----------------------------------------------------------*/
673
674 void vPortEnterCritical( void )
675 {
676     if( xPortRunning == pdTRUE )
677     {
678         /* The interrupt event mutex is held for the entire critical section,
679          * effectively disabling (simulated) interrupts. */
680         WaitForSingleObject( pvInterruptEventMutex, INFINITE );
681     }
682
683     ulCriticalNesting++;
684 }
685 /*-----------------------------------------------------------*/
686
687 void vPortExitCritical( void )
688 {
689     int32_t lMutexNeedsReleasing;
690
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;
694
695     if( ulCriticalNesting > portNO_CRITICAL_NESTING )
696     {
697         ulCriticalNesting--;
698
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 ) )
702         {
703             /* Were any interrupts set to pending while interrupts were
704              * (simulated) disabled? */
705             if( ulPendingInterrupts != 0UL )
706             {
707                 ThreadState_t * pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
708
709                 configASSERT( xPortRunning );
710
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
717                  * here. */
718                 SetEvent( pvInterruptEvent );
719
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 );
725
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 );
731             }
732         }
733     }
734
735     if( pvInterruptEventMutex != NULL )
736     {
737         if( lMutexNeedsReleasing == pdTRUE )
738         {
739             configASSERT( xPortRunning );
740             ReleaseMutex( pvInterruptEventMutex );
741         }
742     }
743 }
744 /*-----------------------------------------------------------*/