1 /*
2  * FreeRTOS Kernel V11.1.0
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 
prvSimulatedPeripheralTimer(LPVOID lpParameter)140 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter )
141 {
142     TickType_t xMinimumWindowsBlockTime;
143     TIMECAPS xTimeCaps;
144 
145     /* Set the timer resolution to the maximum possible. */
146     if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )
147     {
148         xMinimumWindowsBlockTime = ( TickType_t ) xTimeCaps.wPeriodMin;
149         timeBeginPeriod( xTimeCaps.wPeriodMin );
150 
151         /* Register an exit handler so the timeBeginPeriod() function can be
152          * matched with a timeEndPeriod() when the application exits. */
153         SetConsoleCtrlHandler( prvEndProcess, TRUE );
154     }
155     else
156     {
157         xMinimumWindowsBlockTime = ( TickType_t ) 20;
158     }
159 
160     /* Just to prevent compiler warnings. */
161     ( void ) lpParameter;
162 
163     while( xPortRunning == pdTRUE )
164     {
165         /* Wait until the timer expires and we can access the simulated interrupt
166          * variables.  *NOTE* this is not a 'real time' way of generating tick
167          * events as the next wake time should be relative to the previous wake
168          * time, not the time that Sleep() is called.  It is done this way to
169          * prevent overruns in this very non real time simulated/emulated
170          * environment. */
171         if( portTICK_PERIOD_MS < xMinimumWindowsBlockTime )
172         {
173             Sleep( xMinimumWindowsBlockTime );
174         }
175         else
176         {
177             Sleep( portTICK_PERIOD_MS );
178         }
179 
180         if( xPortRunning == pdTRUE )
181         {
182             configASSERT( xPortRunning );
183 
184             /* Can't proceed if in a critical section as pvInterruptEventMutex won't
185              * be available. */
186             WaitForSingleObject( pvInterruptEventMutex, INFINITE );
187 
188             /* The timer has expired, generate the simulated tick event. */
189             ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );
190 
191             /* The interrupt is now pending - notify the simulated interrupt
192              * handler thread.  Must be outside of a critical section to get here so
193              * the handler thread can execute immediately pvInterruptEventMutex is
194              * released. */
195             configASSERT( ulCriticalNesting == 0UL );
196             SetEvent( pvInterruptEvent );
197 
198             /* Give back the mutex so the simulated interrupt handler unblocks
199              * and can access the interrupt handler variables. */
200             ReleaseMutex( pvInterruptEventMutex );
201         }
202     }
203 
204     return 0;
205 }
206 /*-----------------------------------------------------------*/
207 
prvEndProcess(DWORD dwCtrlType)208 static BOOL WINAPI prvEndProcess( DWORD dwCtrlType )
209 {
210     TIMECAPS xTimeCaps;
211 
212     ( void ) dwCtrlType;
213 
214     if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )
215     {
216         /* Match the call to timeBeginPeriod( xTimeCaps.wPeriodMin ) made when
217          * the process started with a timeEndPeriod() as the process exits. */
218         timeEndPeriod( xTimeCaps.wPeriodMin );
219     }
220 
221     return pdFALSE;
222 }
223 /*-----------------------------------------------------------*/
224 
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)225 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
226                                      TaskFunction_t pxCode,
227                                      void * pvParameters )
228 {
229     ThreadState_t * pxThreadState = NULL;
230     int8_t * pcTopOfStack = ( int8_t * ) pxTopOfStack;
231     const SIZE_T xStackSize = 1024; /* Set the size to a small number which will get rounded up to the minimum possible. */
232 
233     /* In this simulated case a stack is not initialised, but instead a thread
234      * is created that will execute the task being created.  The thread handles
235      * the context switching itself.  The ThreadState_t object is placed onto
236      * the stack that was created for the task - so the stack buffer is still
237      * used, just not in the conventional way.  It will not be used for anything
238      * other than holding this structure. */
239     pxThreadState = ( ThreadState_t * ) ( pcTopOfStack - sizeof( ThreadState_t ) );
240 
241     /* Create the event used to prevent the thread from executing past its yield
242      * point if the SuspendThread() call that suspends the thread does not take
243      * effect immediately (it is an asynchronous call). */
244     pxThreadState->pvYieldEvent = CreateEvent( NULL,   /* Default security attributes. */
245                                                FALSE,  /* Auto reset. */
246                                                FALSE,  /* Start not signalled. */
247                                                NULL ); /* No name. */
248 
249 
250 #ifdef __GNUC__
251     /* GCC reports the warning for the cast operation from TaskFunction_t to LPTHREAD_START_ROUTINE. */
252     /* Disable this warning here by the #pragma option. */
253 #pragma GCC diagnostic push
254 #pragma GCC diagnostic ignored "-Wcast-function-type"
255 #endif
256     /* Create the thread itself. */
257     pxThreadState->pvThread = CreateThread( NULL, xStackSize, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, NULL );
258 #ifdef __GNUC__
259 #pragma GCC diagnostic pop
260 #endif
261 
262     configASSERT( pxThreadState->pvThread ); /* See comment where TerminateThread() is called. */
263     SetThreadAffinityMask( pxThreadState->pvThread, 0x01 );
264     SetThreadPriorityBoost( pxThreadState->pvThread, TRUE );
265     SetThreadPriority( pxThreadState->pvThread, portTASK_THREAD_PRIORITY );
266 
267     return ( StackType_t * ) pxThreadState;
268 }
269 /*-----------------------------------------------------------*/
270 
xPortStartScheduler(void)271 BaseType_t xPortStartScheduler( void )
272 {
273     void * pvHandle = NULL;
274     int32_t lSuccess;
275     ThreadState_t * pxThreadState = NULL;
276     SYSTEM_INFO xSystemInfo;
277 
278     /* This port runs windows threads with extremely high priority.  All the
279      * threads execute on the same core - to prevent locking up the host only start
280      * if the host has multiple cores. */
281     GetSystemInfo( &xSystemInfo );
282 
283     if( xSystemInfo.dwNumberOfProcessors <= 1 )
284     {
285         printf( "This version of the FreeRTOS Windows port can only be used on multi-core hosts.\r\n" );
286         lSuccess = pdFAIL;
287     }
288     else
289     {
290         lSuccess = pdPASS;
291 
292         /* The highest priority class is used to [try to] prevent other Windows
293          * activity interfering with FreeRTOS timing too much. */
294         if( SetPriorityClass( GetCurrentProcess(), REALTIME_PRIORITY_CLASS ) == 0 )
295         {
296             printf( "SetPriorityClass() failed\r\n" );
297         }
298 
299         /* Install the interrupt handlers used by the scheduler itself. */
300         vPortSetInterruptHandler( portINTERRUPT_YIELD, prvProcessYieldInterrupt );
301         vPortSetInterruptHandler( portINTERRUPT_TICK, prvProcessTickInterrupt );
302 
303         /* Create the events and mutexes that are used to synchronise all the
304          * threads. */
305         pvInterruptEventMutex = CreateMutex( NULL, FALSE, NULL );
306         pvInterruptEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
307 
308         if( ( pvInterruptEventMutex == NULL ) || ( pvInterruptEvent == NULL ) )
309         {
310             lSuccess = pdFAIL;
311         }
312 
313         /* Set the priority of this thread such that it is above the priority of
314          * the threads that run tasks.  This higher priority is required to ensure
315          * simulated interrupts take priority over tasks. */
316         pvHandle = GetCurrentThread();
317 
318         if( pvHandle == NULL )
319         {
320             lSuccess = pdFAIL;
321         }
322     }
323 
324     if( lSuccess == pdPASS )
325     {
326         if( SetThreadPriority( pvHandle, portSIMULATED_INTERRUPTS_THREAD_PRIORITY ) == 0 )
327         {
328             lSuccess = pdFAIL;
329         }
330 
331         SetThreadPriorityBoost( pvHandle, TRUE );
332         SetThreadAffinityMask( pvHandle, 0x01 );
333     }
334 
335     if( lSuccess == pdPASS )
336     {
337         /* Start the thread that simulates the timer peripheral to generate
338          * tick interrupts.  The priority is set below that of the simulated
339          * interrupt handler so the interrupt event mutex is used for the
340          * handshake / overrun protection. */
341         pvHandle = CreateThread( NULL, 0, prvSimulatedPeripheralTimer, NULL, CREATE_SUSPENDED, NULL );
342 
343         if( pvHandle != NULL )
344         {
345             SetThreadPriority( pvHandle, portSIMULATED_TIMER_THREAD_PRIORITY );
346             SetThreadPriorityBoost( pvHandle, TRUE );
347             SetThreadAffinityMask( pvHandle, 0x01 );
348             ResumeThread( pvHandle );
349         }
350 
351         /* Start the highest priority task by obtaining its associated thread
352          * state structure, in which is stored the thread handle. */
353         pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
354         ulCriticalNesting = portNO_CRITICAL_NESTING;
355 
356         /* Start the first task. */
357         ResumeThread( pxThreadState->pvThread );
358 
359         /* The scheduler is now running. */
360         xPortRunning = pdTRUE;
361 
362         /* Handle all simulated interrupts - including yield requests and
363          * simulated ticks. */
364         prvProcessSimulatedInterrupts();
365     }
366 
367     /* Would not expect to return from prvProcessSimulatedInterrupts(), so should
368      * not get here. */
369     return 0;
370 }
371 /*-----------------------------------------------------------*/
372 
prvProcessYieldInterrupt(void)373 static uint32_t prvProcessYieldInterrupt( void )
374 {
375     /* Always return true as this is a yield. */
376     return pdTRUE;
377 }
378 /*-----------------------------------------------------------*/
379 
prvProcessTickInterrupt(void)380 static uint32_t prvProcessTickInterrupt( void )
381 {
382     uint32_t ulSwitchRequired;
383 
384     /* Process the tick itself. */
385     configASSERT( xPortRunning );
386     ulSwitchRequired = ( uint32_t ) xTaskIncrementTick();
387 
388     return ulSwitchRequired;
389 }
390 /*-----------------------------------------------------------*/
391 
prvProcessSimulatedInterrupts(void)392 static void prvProcessSimulatedInterrupts( void )
393 {
394     uint32_t ulSwitchRequired, i;
395     ThreadState_t * pxThreadState;
396     void * pvObjectList[ 2 ];
397     CONTEXT xContext;
398     DWORD xWinApiResult;
399     const DWORD xTimeoutMilliseconds = 1000;
400 
401     /* Going to block on the mutex that ensured exclusive access to the simulated
402      * interrupt objects, and the event that signals that a simulated interrupt
403      * should be processed. */
404     pvObjectList[ 0 ] = pvInterruptEventMutex;
405     pvObjectList[ 1 ] = pvInterruptEvent;
406 
407     /* Create a pending tick to ensure the first task is started as soon as
408      * this thread pends. */
409     ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );
410     SetEvent( pvInterruptEvent );
411 
412     while( xPortRunning == pdTRUE )
413     {
414         xInsideInterrupt = pdFALSE;
415 
416         /* Wait with timeout so that we can exit from this loop when
417          * the scheduler is stopped by calling vPortEndScheduler. */
418         xWinApiResult = WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, xTimeoutMilliseconds );
419 
420         if( xWinApiResult != WAIT_TIMEOUT )
421         {
422             /* Cannot be in a critical section to get here.  Tasks that exit a
423              * critical section will block on a yield mutex to wait for an interrupt to
424              * process if an interrupt was set pending while the task was inside the
425              * critical section.  xInsideInterrupt prevents interrupts that contain
426              * critical sections from doing the same. */
427             xInsideInterrupt = pdTRUE;
428 
429             /* Used to indicate whether the simulated interrupt processing has
430              * necessitated a context switch to another task/thread. */
431             ulSwitchRequired = pdFALSE;
432 
433             /* For each interrupt we are interested in processing, each of which is
434              * represented by a bit in the 32bit ulPendingInterrupts variable. */
435             for( i = 0; i < portMAX_INTERRUPTS; i++ )
436             {
437                 /* Is the simulated interrupt pending? */
438                 if( ( ulPendingInterrupts & ( 1UL << i ) ) != 0 )
439                 {
440                     /* Is a handler installed? */
441                     if( ulIsrHandler[ i ] != NULL )
442                     {
443                         /* Run the actual handler.  Handlers return pdTRUE if they
444                          * necessitate a context switch. */
445                         if( ulIsrHandler[ i ]() != pdFALSE )
446                         {
447                             /* A bit mask is used purely to help debugging. */
448                             ulSwitchRequired |= ( 1 << i );
449                         }
450                     }
451 
452                     /* Clear the interrupt pending bit. */
453                     ulPendingInterrupts &= ~( 1UL << i );
454                 }
455             }
456 
457             if( ulSwitchRequired != pdFALSE )
458             {
459                 void * pvOldCurrentTCB;
460 
461                 pvOldCurrentTCB = pxCurrentTCB;
462 
463                 /* Select the next task to run. */
464                 vTaskSwitchContext();
465 
466                 /* If the task selected to enter the running state is not the task
467                  * that is already in the running state. */
468                 if( pvOldCurrentTCB != pxCurrentTCB )
469                 {
470                     /* Suspend the old thread.  In the cases where the (simulated)
471                      * interrupt is asynchronous (tick event swapping a task out rather
472                      * than a task blocking or yielding) it doesn't matter if the
473                      * 'suspend' operation doesn't take effect immediately - if it
474                      * doesn't it would just be like the interrupt occurring slightly
475                      * later.  In cases where the yield was caused by a task blocking
476                      * or yielding then the task will block on a yield event after the
477                      * yield operation in case the 'suspend' operation doesn't take
478                      * effect immediately.  */
479                     pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pvOldCurrentTCB );
480                     SuspendThread( pxThreadState->pvThread );
481 
482                     /* Ensure the thread is actually suspended by performing a
483                      *  synchronous operation that can only complete when the thread is
484                      *  actually suspended.  The below code asks for dummy register
485                      *  data.  Experimentation shows that these two lines don't appear
486                      *  to do anything now, but according to
487                      *  https://devblogs.microsoft.com/oldnewthing/20150205-00/?p=44743
488                      *  they do - so as they do not harm (slight run-time hit). */
489                     xContext.ContextFlags = CONTEXT_INTEGER;
490                     ( void ) GetThreadContext( pxThreadState->pvThread, &xContext );
491 
492                     /* Obtain the state of the task now selected to enter the
493                      * Running state. */
494                     pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pxCurrentTCB );
495 
496                     /* pxThreadState->pvThread can be NULL if the task deleted
497                      * itself - but a deleted task should never be resumed here. */
498                     configASSERT( pxThreadState->pvThread != NULL );
499                     ResumeThread( pxThreadState->pvThread );
500                 }
501             }
502 
503             /* If the thread that is about to be resumed stopped running
504              * because it yielded then it will wait on an event when it resumed
505              * (to ensure it does not continue running after the call to
506              * SuspendThread() above as SuspendThread() is asynchronous).
507              * Signal the event to ensure the thread can proceed now it is
508              * valid for it to do so.  Signaling the event is benign in the case that
509              * the task was switched out asynchronously by an interrupt as the event
510              * is reset before the task blocks on it. */
511             pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pxCurrentTCB );
512             SetEvent( pxThreadState->pvYieldEvent );
513             ReleaseMutex( pvInterruptEventMutex );
514         }
515     }
516 }
517 /*-----------------------------------------------------------*/
518 
vPortDeleteThread(void * pvTaskToDelete)519 void vPortDeleteThread( void * pvTaskToDelete )
520 {
521     ThreadState_t * pxThreadState;
522     uint32_t ulErrorCode;
523 
524     /* Remove compiler warnings if configASSERT() is not defined. */
525     ( void ) ulErrorCode;
526 
527     /* Find the handle of the thread being deleted. */
528     pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pvTaskToDelete );
529 
530     /* Check that the thread is still valid, it might have been closed by
531      * vPortCloseRunningThread() - which will be the case if the task associated
532      * with the thread originally deleted itself rather than being deleted by a
533      * different task. */
534     if( pxThreadState->pvThread != NULL )
535     {
536         WaitForSingleObject( pvInterruptEventMutex, INFINITE );
537 
538         /* !!! This is not a nice way to terminate a thread, and will eventually
539          * result in resources being depleted if tasks frequently delete other
540          * tasks (rather than deleting themselves) as the task stacks will not be
541          * freed. */
542         ulErrorCode = TerminateThread( pxThreadState->pvThread, 0 );
543         configASSERT( ulErrorCode );
544 
545         ulErrorCode = CloseHandle( pxThreadState->pvThread );
546         configASSERT( ulErrorCode );
547 
548         ReleaseMutex( pvInterruptEventMutex );
549     }
550 }
551 /*-----------------------------------------------------------*/
552 
vPortCloseRunningThread(void * pvTaskToDelete,volatile BaseType_t * pxPendYield)553 void vPortCloseRunningThread( void * pvTaskToDelete,
554                               volatile BaseType_t * pxPendYield )
555 {
556     ThreadState_t * pxThreadState;
557     void * pvThread;
558     uint32_t ulErrorCode;
559 
560     /* Remove compiler warnings if configASSERT() is not defined. */
561     ( void ) ulErrorCode;
562 
563     /* Find the handle of the thread being deleted. */
564     pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pvTaskToDelete );
565     pvThread = pxThreadState->pvThread;
566 
567     /* Raise the Windows priority of the thread to ensure the FreeRTOS scheduler
568      * does not run and swap it out before it is closed.  If that were to happen
569      * the thread would never run again and effectively be a thread handle and
570      * memory leak. */
571     SetThreadPriority( pvThread, portDELETE_SELF_THREAD_PRIORITY );
572 
573     /* This function will not return, therefore a yield is set as pending to
574      * ensure a context switch occurs away from this thread on the next tick. */
575     *pxPendYield = pdTRUE;
576 
577     /* Mark the thread associated with this task as invalid so
578      * vPortDeleteThread() does not try to terminate it. */
579     pxThreadState->pvThread = NULL;
580 
581     /* Close the thread. */
582     ulErrorCode = CloseHandle( pvThread );
583     configASSERT( ulErrorCode );
584 
585     /* This is called from a critical section, which must be exited before the
586      * thread stops. */
587     taskEXIT_CRITICAL();
588     CloseHandle( pxThreadState->pvYieldEvent );
589     ExitThread( 0 );
590 }
591 /*-----------------------------------------------------------*/
592 
vPortEndScheduler(void)593 void vPortEndScheduler( void )
594 {
595     xPortRunning = pdFALSE;
596 }
597 /*-----------------------------------------------------------*/
598 
vPortGenerateSimulatedInterrupt(uint32_t ulInterruptNumber)599 void vPortGenerateSimulatedInterrupt( uint32_t ulInterruptNumber )
600 {
601     ThreadState_t * pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
602 
603     configASSERT( xPortRunning );
604 
605     if( ( ulInterruptNumber < portMAX_INTERRUPTS ) && ( pvInterruptEventMutex != NULL ) )
606     {
607         WaitForSingleObject( pvInterruptEventMutex, INFINITE );
608         ulPendingInterrupts |= ( 1 << ulInterruptNumber );
609 
610         /* The simulated interrupt is now held pending, but don't actually
611          * process it yet if this call is within a critical section.  It is
612          * possible for this to be in a critical section as calls to wait for
613          * mutexes are accumulative.  If in a critical section then the event
614          * will get set when the critical section nesting count is wound back
615          * down to zero. */
616         if( ulCriticalNesting == portNO_CRITICAL_NESTING )
617         {
618             SetEvent( pvInterruptEvent );
619 
620             /* Going to wait for an event - make sure the event is not already
621              * signaled. */
622             ResetEvent( pxThreadState->pvYieldEvent );
623         }
624 
625         ReleaseMutex( pvInterruptEventMutex );
626 
627         if( ulCriticalNesting == portNO_CRITICAL_NESTING )
628         {
629             /* An interrupt was pended so ensure to block to allow it to
630              * execute.  In most cases the (simulated) interrupt will have
631              * executed before the next line is reached - so this is just to make
632              * sure. */
633             WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );
634         }
635     }
636 }
637 /*-----------------------------------------------------------*/
638 
vPortSetInterruptHandler(uint32_t ulInterruptNumber,uint32_t (* pvHandler)(void))639 void vPortSetInterruptHandler( uint32_t ulInterruptNumber,
640                                uint32_t ( * pvHandler )( void ) )
641 {
642     if( ulInterruptNumber < portMAX_INTERRUPTS )
643     {
644         if( pvInterruptEventMutex != NULL )
645         {
646             WaitForSingleObject( pvInterruptEventMutex, INFINITE );
647             ulIsrHandler[ ulInterruptNumber ] = pvHandler;
648             ReleaseMutex( pvInterruptEventMutex );
649         }
650         else
651         {
652             ulIsrHandler[ ulInterruptNumber ] = pvHandler;
653         }
654     }
655 }
656 /*-----------------------------------------------------------*/
657 
vPortEnterCritical(void)658 void vPortEnterCritical( void )
659 {
660     if( xPortRunning == pdTRUE )
661     {
662         /* The interrupt event mutex is held for the entire critical section,
663          * effectively disabling (simulated) interrupts. */
664         WaitForSingleObject( pvInterruptEventMutex, INFINITE );
665     }
666 
667     ulCriticalNesting++;
668 }
669 /*-----------------------------------------------------------*/
670 
vPortExitCritical(void)671 void vPortExitCritical( void )
672 {
673     int32_t lMutexNeedsReleasing;
674 
675     /* The interrupt event mutex should already be held by this thread as it was
676      * obtained on entry to the critical section. */
677     lMutexNeedsReleasing = pdTRUE;
678 
679     if( ulCriticalNesting > portNO_CRITICAL_NESTING )
680     {
681         ulCriticalNesting--;
682 
683         /* Don't need to wait for any pending interrupts to execute if the
684          * critical section was exited from inside an interrupt. */
685         if( ( ulCriticalNesting == portNO_CRITICAL_NESTING ) && ( xInsideInterrupt == pdFALSE ) )
686         {
687             /* Were any interrupts set to pending while interrupts were
688              * (simulated) disabled? */
689             if( ulPendingInterrupts != 0UL )
690             {
691                 ThreadState_t * pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
692 
693                 configASSERT( xPortRunning );
694 
695                 /* The interrupt won't actually executed until
696                  * pvInterruptEventMutex is released as it waits on both
697                  * pvInterruptEventMutex and pvInterruptEvent.
698                  * pvInterruptEvent is only set when the simulated
699                  * interrupt is pended if the interrupt is pended
700                  * from outside a critical section - hence it is set
701                  * here. */
702                 SetEvent( pvInterruptEvent );
703 
704                 /* The calling task is going to wait for an event to ensure the
705                  * interrupt that is pending executes immediately after the
706                  * critical section is exited - so make sure the event is not
707                  * already signaled. */
708                 ResetEvent( pxThreadState->pvYieldEvent );
709 
710                 /* Mutex will be released now so the (simulated) interrupt can
711                  * execute, so does not require releasing on function exit. */
712                 lMutexNeedsReleasing = pdFALSE;
713                 ReleaseMutex( pvInterruptEventMutex );
714                 WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );
715             }
716         }
717     }
718 
719     if( pvInterruptEventMutex != NULL )
720     {
721         if( lMutexNeedsReleasing == pdTRUE )
722         {
723             configASSERT( xPortRunning );
724             ReleaseMutex( pvInterruptEventMutex );
725         }
726     }
727 }
728 /*-----------------------------------------------------------*/
729