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 WIN32_LEAN_AND_MEAN
37     #include <winsock2.h>
38 #else
39     #include <winsock.h>
40 #endif
41 
42 #include <timeapi.h>
43 
44 #ifdef __GNUC__
45     #include "mmsystem.h"
46 #else
47     #pragma comment(lib, "winmm.lib")
48 #endif
49 
50 #define portMAX_INTERRUPTS                          ( ( uint32_t ) sizeof( uint32_t ) * 8UL ) /* The number of bits in an uint32_t. */
51 #define portNO_CRITICAL_NESTING                     ( ( uint32_t ) 0 )
52 
53 /* The priorities at which the various components of the simulation execute. */
54 #define portDELETE_SELF_THREAD_PRIORITY             THREAD_PRIORITY_TIME_CRITICAL /* Must be highest. */
55 #define portSIMULATED_INTERRUPTS_THREAD_PRIORITY    THREAD_PRIORITY_TIME_CRITICAL
56 #define portSIMULATED_TIMER_THREAD_PRIORITY         THREAD_PRIORITY_HIGHEST
57 #define portTASK_THREAD_PRIORITY                    THREAD_PRIORITY_ABOVE_NORMAL
58 
59 /*
60  * Created as a high priority thread, this function uses a timer to simulate
61  * a tick interrupt being generated on an embedded target.  In this Windows
62  * environment the timer does not achieve anything approaching real time
63  * performance though.
64  */
65 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter );
66 
67 /*
68  * Process all the simulated interrupts - each represented by a bit in
69  * ulPendingInterrupts variable.
70  */
71 static void prvProcessSimulatedInterrupts( void );
72 
73 /*
74  * Interrupt handlers used by the kernel itself.  These are executed from the
75  * simulated interrupt handler thread.
76  */
77 static uint32_t prvProcessYieldInterrupt( void );
78 static uint32_t prvProcessTickInterrupt( void );
79 
80 /*
81  * Exiting a critical section will cause the calling task to block on yield
82  * event to wait for an interrupt to process if an interrupt was pended while
83  * inside the critical section.  This variable protects against a recursive
84  * attempt to obtain pvInterruptEventMutex if a critical section is used inside
85  * an interrupt handler itself.
86  */
87 volatile BaseType_t xInsideInterrupt = pdFALSE;
88 
89 /*
90  * Called when the process exits to let Windows know the high timer resolution
91  * is no longer required.
92  */
93 static BOOL WINAPI prvEndProcess( DWORD dwCtrlType );
94 
95 /*-----------------------------------------------------------*/
96 
97 /* The WIN32 simulator runs each task in a thread.  The context switching is
98  * managed by the threads, so the task stack does not have to be managed directly,
99  * although the task stack is still used to hold an xThreadState structure this is
100  * the only thing it will ever hold.  The structure indirectly maps the task handle
101  * to a thread handle. */
102 typedef struct
103 {
104     /* Handle of the thread that executes the task. */
105     void * pvThread;
106 
107     /* Event used to make sure the thread does not execute past a yield point
108      * between the call to SuspendThread() to suspend the thread and the
109      * asynchronous SuspendThread() operation actually being performed. */
110     void * pvYieldEvent;
111 } ThreadState_t;
112 
113 /* Simulated interrupts waiting to be processed.  This is a bit mask where each
114  * bit represents one interrupt, so a maximum of 32 interrupts can be simulated. */
115 static volatile uint32_t ulPendingInterrupts = 0UL;
116 
117 /* An event used to inform the simulated interrupt processing thread (a high
118  * priority thread that simulated interrupt processing) that an interrupt is
119  * pending. */
120 static void * pvInterruptEvent = NULL;
121 
122 /* Mutex used to protect all the simulated interrupt variables that are accessed
123  * by multiple threads. */
124 static void * pvInterruptEventMutex = NULL;
125 
126 /* The critical nesting count for the currently executing task.  This is
127  * initialised to a non-zero value so interrupts do not become enabled during
128  * the initialisation phase.  As each task has its own critical nesting value
129  * ulCriticalNesting will get set to zero when the first task runs.  This
130  * initialisation is probably not critical in this simulated environment as the
131  * simulated interrupt handlers do not get created until the FreeRTOS scheduler is
132  * started anyway. */
133 static volatile uint32_t ulCriticalNesting = 9999UL;
134 
135 /* Handlers for all the simulated software interrupts.  The first two positions
136  * are used for the Yield and Tick interrupts so are handled slightly differently,
137  * all the other interrupts can be user defined. */
138 static uint32_t (* ulIsrHandler[ portMAX_INTERRUPTS ])( void ) = { 0 };
139 
140 /* Pointer to the TCB of the currently executing task. */
141 extern void * volatile pxCurrentTCB;
142 
143 /* Used to ensure nothing is processed during the startup sequence. */
144 static BaseType_t xPortRunning = pdFALSE;
145 
146 /*-----------------------------------------------------------*/
147 
prvSimulatedPeripheralTimer(LPVOID lpParameter)148 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter )
149 {
150     TickType_t xMinimumWindowsBlockTime;
151     TIMECAPS xTimeCaps;
152     TickType_t xWaitTimeBetweenTicks = portTICK_PERIOD_MS;
153     HANDLE hTimer = NULL;
154     LARGE_INTEGER liDueTime;
155     BOOL bSuccess;
156 
157     /* Set the timer resolution to the maximum possible. */
158     if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )
159     {
160         xMinimumWindowsBlockTime = ( TickType_t ) xTimeCaps.wPeriodMin;
161         timeBeginPeriod( xTimeCaps.wPeriodMin );
162 
163         /* Register an exit handler so the timeBeginPeriod() function can be
164          * matched with a timeEndPeriod() when the application exits. */
165         SetConsoleCtrlHandler( prvEndProcess, TRUE );
166     }
167     else
168     {
169         xMinimumWindowsBlockTime = ( TickType_t ) 20;
170     }
171 
172     /* Just to prevent compiler warnings. */
173     ( void ) lpParameter;
174 
175     /* Tick time for the timer is adjusted with the maximum available
176      resolution. */
177     if( portTICK_PERIOD_MS < xMinimumWindowsBlockTime )
178     {
179         xWaitTimeBetweenTicks = xMinimumWindowsBlockTime;
180     }
181 
182     /* Convert the tick time in milliseconds to nanoseconds resolution
183      for the Waitable Timer. */
184     liDueTime.u.LowPart = xWaitTimeBetweenTicks * 1000 * 1000;
185     liDueTime.u.HighPart = 0;
186 
187     /* Create a synchronization Waitable Timer.*/
188     hTimer = CreateWaitableTimer( NULL, FALSE, NULL );
189 
190     configASSERT( hTimer != NULL );
191 
192     /* Set the Waitable Timer. The timer is set to run periodically at every
193     xWaitTimeBetweenTicks milliseconds. */
194     bSuccess = SetWaitableTimer( hTimer, &liDueTime, xWaitTimeBetweenTicks, NULL, NULL, 0 );
195     configASSERT( bSuccess );
196 
197     while( xPortRunning == pdTRUE )
198     {
199         /* Wait until the timer expires and we can access the simulated interrupt
200          * variables. */
201 
202         WaitForSingleObject( hTimer, INFINITE );
203 
204         vPortGenerateSimulatedInterruptFromWindowsThread( portINTERRUPT_TICK );
205     }
206 
207     return 0;
208 }
209 /*-----------------------------------------------------------*/
210 
prvEndProcess(DWORD dwCtrlType)211 static BOOL WINAPI prvEndProcess( DWORD dwCtrlType )
212 {
213     TIMECAPS xTimeCaps;
214 
215     ( void ) dwCtrlType;
216 
217     if( timeGetDevCaps( &xTimeCaps, sizeof( xTimeCaps ) ) == MMSYSERR_NOERROR )
218     {
219         /* Match the call to timeBeginPeriod( xTimeCaps.wPeriodMin ) made when
220          * the process started with a timeEndPeriod() as the process exits. */
221         timeEndPeriod( xTimeCaps.wPeriodMin );
222     }
223 
224     return pdFALSE;
225 }
226 /*-----------------------------------------------------------*/
227 
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)228 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
229                                      TaskFunction_t pxCode,
230                                      void * pvParameters )
231 {
232     ThreadState_t * pxThreadState = NULL;
233     int8_t * pcTopOfStack = ( int8_t * ) pxTopOfStack;
234     const SIZE_T xStackSize = 1024; /* Set the size to a small number which will get rounded up to the minimum possible. */
235 
236     /* In this simulated case a stack is not initialised, but instead a thread
237      * is created that will execute the task being created.  The thread handles
238      * the context switching itself.  The ThreadState_t object is placed onto
239      * the stack that was created for the task - so the stack buffer is still
240      * used, just not in the conventional way.  It will not be used for anything
241      * other than holding this structure. */
242     pxThreadState = ( ThreadState_t * ) ( pcTopOfStack - sizeof( ThreadState_t ) );
243 
244     /* Create the event used to prevent the thread from executing past its yield
245      * point if the SuspendThread() call that suspends the thread does not take
246      * effect immediately (it is an asynchronous call). */
247     pxThreadState->pvYieldEvent = CreateEvent( NULL,   /* Default security attributes. */
248                                                FALSE,  /* Auto reset. */
249                                                FALSE,  /* Start not signalled. */
250                                                NULL ); /* No name. */
251 
252 
253 #ifdef __GNUC__
254     /* GCC reports the warning for the cast operation from TaskFunction_t to LPTHREAD_START_ROUTINE. */
255     /* Disable this warning here by the #pragma option. */
256 #pragma GCC diagnostic push
257 #pragma GCC diagnostic ignored "-Wcast-function-type"
258 #endif
259     /* Create the thread itself. */
260     pxThreadState->pvThread = CreateThread( NULL, xStackSize, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, NULL );
261 #ifdef __GNUC__
262 #pragma GCC diagnostic pop
263 #endif
264 
265     configASSERT( pxThreadState->pvThread ); /* See comment where TerminateThread() is called. */
266     SetThreadAffinityMask( pxThreadState->pvThread, 0x01 );
267     SetThreadPriorityBoost( pxThreadState->pvThread, TRUE );
268     SetThreadPriority( pxThreadState->pvThread, portTASK_THREAD_PRIORITY );
269 
270     return ( StackType_t * ) pxThreadState;
271 }
272 /*-----------------------------------------------------------*/
273 
xPortStartScheduler(void)274 BaseType_t xPortStartScheduler( void )
275 {
276     void * pvHandle = NULL;
277     int32_t lSuccess;
278     ThreadState_t * pxThreadState = NULL;
279     SYSTEM_INFO xSystemInfo;
280 
281     /* This port runs windows threads with extremely high priority.  All the
282      * threads execute on the same core - to prevent locking up the host only start
283      * if the host has multiple cores. */
284     GetSystemInfo( &xSystemInfo );
285 
286     if( xSystemInfo.dwNumberOfProcessors <= 1 )
287     {
288         printf( "This version of the FreeRTOS Windows port can only be used on multi-core hosts.\r\n" );
289         lSuccess = pdFAIL;
290     }
291     else
292     {
293         lSuccess = pdPASS;
294 
295         /* The highest priority class is used to [try to] prevent other Windows
296          * activity interfering with FreeRTOS timing too much. */
297         if( SetPriorityClass( GetCurrentProcess(), REALTIME_PRIORITY_CLASS ) == 0 )
298         {
299             printf( "SetPriorityClass() failed\r\n" );
300         }
301 
302         /* Install the interrupt handlers used by the scheduler itself. */
303         vPortSetInterruptHandler( portINTERRUPT_YIELD, prvProcessYieldInterrupt );
304         vPortSetInterruptHandler( portINTERRUPT_TICK, prvProcessTickInterrupt );
305 
306         /* Create the events and mutexes that are used to synchronise all the
307          * threads. */
308         pvInterruptEventMutex = CreateMutex( NULL, FALSE, NULL );
309         pvInterruptEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
310 
311         if( ( pvInterruptEventMutex == NULL ) || ( pvInterruptEvent == NULL ) )
312         {
313             lSuccess = pdFAIL;
314         }
315 
316         /* Set the priority of this thread such that it is above the priority of
317          * the threads that run tasks.  This higher priority is required to ensure
318          * simulated interrupts take priority over tasks. */
319         pvHandle = GetCurrentThread();
320 
321         if( pvHandle == NULL )
322         {
323             lSuccess = pdFAIL;
324         }
325     }
326 
327     if( lSuccess == pdPASS )
328     {
329         if( SetThreadPriority( pvHandle, portSIMULATED_INTERRUPTS_THREAD_PRIORITY ) == 0 )
330         {
331             lSuccess = pdFAIL;
332         }
333 
334         SetThreadPriorityBoost( pvHandle, TRUE );
335         SetThreadAffinityMask( pvHandle, 0x01 );
336     }
337 
338     if( lSuccess == pdPASS )
339     {
340         /* Start the thread that simulates the timer peripheral to generate
341          * tick interrupts.  The priority is set below that of the simulated
342          * interrupt handler so the interrupt event mutex is used for the
343          * handshake / overrun protection. */
344         pvHandle = CreateThread( NULL, 0, prvSimulatedPeripheralTimer, NULL, CREATE_SUSPENDED, NULL );
345 
346         if( pvHandle != NULL )
347         {
348             SetThreadPriority( pvHandle, portSIMULATED_TIMER_THREAD_PRIORITY );
349             SetThreadPriorityBoost( pvHandle, TRUE );
350             SetThreadAffinityMask( pvHandle, 0x01 );
351             ResumeThread( pvHandle );
352         }
353 
354         /* Start the highest priority task by obtaining its associated thread
355          * state structure, in which is stored the thread handle. */
356         pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
357         ulCriticalNesting = portNO_CRITICAL_NESTING;
358 
359         /* The scheduler is now running. */
360         xPortRunning = pdTRUE;
361 
362         /* Start the first task. */
363         ResumeThread( pxThreadState->pvThread );
364 
365         /* Handle all simulated interrupts - including yield requests and
366          * simulated ticks. */
367         prvProcessSimulatedInterrupts();
368     }
369 
370     /* Would not expect to return from prvProcessSimulatedInterrupts(), so should
371      * not get here. */
372     return 0;
373 }
374 /*-----------------------------------------------------------*/
375 
prvProcessYieldInterrupt(void)376 static uint32_t prvProcessYieldInterrupt( void )
377 {
378     /* Always return true as this is a yield. */
379     return pdTRUE;
380 }
381 /*-----------------------------------------------------------*/
382 
prvProcessTickInterrupt(void)383 static uint32_t prvProcessTickInterrupt( void )
384 {
385     uint32_t ulSwitchRequired;
386 
387     /* Process the tick itself. */
388     configASSERT( xPortRunning );
389     ulSwitchRequired = ( uint32_t ) xTaskIncrementTick();
390 
391     return ulSwitchRequired;
392 }
393 /*-----------------------------------------------------------*/
394 
prvProcessSimulatedInterrupts(void)395 static void prvProcessSimulatedInterrupts( void )
396 {
397     uint32_t ulSwitchRequired, i;
398     ThreadState_t * pxThreadState;
399     void * pvObjectList[ 2 ];
400     CONTEXT xContext;
401     DWORD xWinApiResult;
402     const DWORD xTimeoutMilliseconds = 1000;
403 
404     /* Going to block on the mutex that ensured exclusive access to the simulated
405      * interrupt objects, and the event that signals that a simulated interrupt
406      * should be processed. */
407     pvObjectList[ 0 ] = pvInterruptEventMutex;
408     pvObjectList[ 1 ] = pvInterruptEvent;
409 
410     /* Create a pending tick to ensure the first task is started as soon as
411      * this thread pends. */
412     ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );
413     SetEvent( pvInterruptEvent );
414 
415     while( xPortRunning == pdTRUE )
416     {
417         xInsideInterrupt = pdFALSE;
418 
419         /* Wait with timeout so that we can exit from this loop when
420          * the scheduler is stopped by calling vPortEndScheduler. */
421         xWinApiResult = WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, xTimeoutMilliseconds );
422 
423         if( xWinApiResult != WAIT_TIMEOUT )
424         {
425             /* Cannot be in a critical section to get here.  Tasks that exit a
426              * critical section will block on a yield mutex to wait for an interrupt to
427              * process if an interrupt was set pending while the task was inside the
428              * critical section.  xInsideInterrupt prevents interrupts that contain
429              * critical sections from doing the same. */
430             xInsideInterrupt = pdTRUE;
431 
432             /* Used to indicate whether the simulated interrupt processing has
433              * necessitated a context switch to another task/thread. */
434             ulSwitchRequired = pdFALSE;
435 
436             /* For each interrupt we are interested in processing, each of which is
437              * represented by a bit in the 32bit ulPendingInterrupts variable. */
438             for( i = 0; i < portMAX_INTERRUPTS; i++ )
439             {
440                 /* Is the simulated interrupt pending? */
441                 if( ( ulPendingInterrupts & ( 1UL << i ) ) != 0 )
442                 {
443                     /* Is a handler installed? */
444                     if( ulIsrHandler[ i ] != NULL )
445                     {
446                         /* Run the actual handler.  Handlers return pdTRUE if they
447                          * necessitate a context switch. */
448                         if( ulIsrHandler[ i ]() != pdFALSE )
449                         {
450                             /* A bit mask is used purely to help debugging. */
451                             ulSwitchRequired |= ( 1 << i );
452                         }
453                     }
454 
455                     /* Clear the interrupt pending bit. */
456                     ulPendingInterrupts &= ~( 1UL << i );
457                 }
458             }
459 
460             if( ulSwitchRequired != pdFALSE )
461             {
462                 /* Suspend the old thread. */
463                 pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
464                 SuspendThread( pxThreadState->pvThread );
465 
466                 /* Ensure the thread is actually suspended by performing a
467                  * synchronous operation that can only complete when the thread
468                  * is actually suspended. The below code asks for dummy register
469                  * data. Experimentation shows that these two lines don't appear
470                  * to do anything now, but according to
471                  * https://devblogs.microsoft.com/oldnewthing/20150205-00/?p=44743
472                  * they do - so as they do not harm (slight run-time hit). */
473                 xContext.ContextFlags = CONTEXT_INTEGER;
474                 ( void ) GetThreadContext( pxThreadState->pvThread, &xContext );
475 
476                 /* Select the next task to run. */
477                 vTaskSwitchContext();
478 
479                 /* Obtain the state of the task now selected to enter the
480                  * Running state. */
481                 pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pxCurrentTCB );
482 
483                 /* pxThreadState->pvThread can be NULL if the task deleted
484                  * itself - but a deleted task should never be resumed here. */
485                 configASSERT( pxThreadState->pvThread != NULL );
486                 ResumeThread( pxThreadState->pvThread );
487             }
488 
489             /* If the thread that is about to be resumed stopped running
490              * because it yielded then it will wait on an event when it resumed
491              * (to ensure it does not continue running after the call to
492              * SuspendThread() above as SuspendThread() is asynchronous).
493              * Signal the event to ensure the thread can proceed now it is
494              * valid for it to do so.  Signaling the event is benign in the case that
495              * the task was switched out asynchronously by an interrupt as the event
496              * is reset before the task blocks on it. */
497             pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pxCurrentTCB );
498             SetEvent( pxThreadState->pvYieldEvent );
499             ReleaseMutex( pvInterruptEventMutex );
500         }
501     }
502 }
503 /*-----------------------------------------------------------*/
504 
vPortDeleteThread(void * pvTaskToDelete)505 void vPortDeleteThread( void * pvTaskToDelete )
506 {
507     ThreadState_t * pxThreadState;
508     uint32_t ulErrorCode;
509 
510     /* Remove compiler warnings if configASSERT() is not defined. */
511     ( void ) ulErrorCode;
512 
513     /* Find the handle of the thread being deleted. */
514     pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pvTaskToDelete );
515 
516     /* Check that the thread is still valid, it might have been closed by
517      * vPortCloseRunningThread() - which will be the case if the task associated
518      * with the thread originally deleted itself rather than being deleted by a
519      * different task. */
520     if( pxThreadState->pvThread != NULL )
521     {
522         WaitForSingleObject( pvInterruptEventMutex, INFINITE );
523 
524         /* !!! This is not a nice way to terminate a thread, and will eventually
525          * result in resources being depleted if tasks frequently delete other
526          * tasks (rather than deleting themselves) as the task stacks will not be
527          * freed. */
528         ulErrorCode = TerminateThread( pxThreadState->pvThread, 0 );
529         configASSERT( ulErrorCode );
530 
531         ulErrorCode = CloseHandle( pxThreadState->pvThread );
532         configASSERT( ulErrorCode );
533 
534         ReleaseMutex( pvInterruptEventMutex );
535     }
536 }
537 /*-----------------------------------------------------------*/
538 
vPortCloseRunningThread(void * pvTaskToDelete,volatile BaseType_t * pxPendYield)539 void vPortCloseRunningThread( void * pvTaskToDelete,
540                               volatile BaseType_t * pxPendYield )
541 {
542     ThreadState_t * pxThreadState;
543     void * pvThread;
544     uint32_t ulErrorCode;
545 
546     /* Remove compiler warnings if configASSERT() is not defined. */
547     ( void ) ulErrorCode;
548 
549     /* Find the handle of the thread being deleted. */
550     pxThreadState = ( ThreadState_t * ) ( *( size_t * ) pvTaskToDelete );
551     pvThread = pxThreadState->pvThread;
552 
553     /* Raise the Windows priority of the thread to ensure the FreeRTOS scheduler
554      * does not run and swap it out before it is closed.  If that were to happen
555      * the thread would never run again and effectively be a thread handle and
556      * memory leak. */
557     SetThreadPriority( pvThread, portDELETE_SELF_THREAD_PRIORITY );
558 
559     /* This function will not return, therefore a yield is set as pending to
560      * ensure a context switch occurs away from this thread on the next tick. */
561     *pxPendYield = pdTRUE;
562 
563     /* Mark the thread associated with this task as invalid so
564      * vPortDeleteThread() does not try to terminate it. */
565     pxThreadState->pvThread = NULL;
566 
567     /* Close the thread. */
568     ulErrorCode = CloseHandle( pvThread );
569     configASSERT( ulErrorCode );
570 
571     /* This is called from a critical section, which must be exited before the
572      * thread stops. */
573     taskEXIT_CRITICAL();
574 
575     /* Record that a yield is pending so that the next tick interrupt switches
576      * out this thread regardless of the value of configUSE_PREEMPTION. This is
577      * needed when a task deletes itself - the taskYIELD_WITHIN_API within
578      * vTaskDelete does not get called because this function never returns. If
579      * we do not pend portINTERRUPT_YIELD here, the next task is not scheduled
580      * when configUSE_PREEMPTION is set to 0. */
581     if( pvInterruptEventMutex != NULL )
582     {
583         WaitForSingleObject( pvInterruptEventMutex, INFINITE );
584         ulPendingInterrupts |= ( 1 << portINTERRUPT_YIELD );
585         ReleaseMutex( pvInterruptEventMutex );
586     }
587 
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 
vPortGenerateSimulatedInterruptFromWindowsThread(uint32_t ulInterruptNumber)639 void vPortGenerateSimulatedInterruptFromWindowsThread( uint32_t ulInterruptNumber )
640 {
641     if( xPortRunning == pdTRUE )
642     {
643         /* Can't proceed if in a critical section as pvInterruptEventMutex won't
644          * be available. */
645         WaitForSingleObject( pvInterruptEventMutex, INFINITE );
646 
647         /* Pending a user defined interrupt to be handled in simulated interrupt
648          * handler thread. */
649         ulPendingInterrupts |= ( 1 << ulInterruptNumber );
650 
651         /* The interrupt is now pending - notify the simulated interrupt
652          * handler thread.  Must be outside of a critical section to get here so
653          * the handler thread can execute immediately pvInterruptEventMutex is
654          * released. */
655         configASSERT( ulCriticalNesting == 0UL );
656         SetEvent( pvInterruptEvent );
657 
658         /* Give back the mutex so the simulated interrupt handler unblocks
659          * and can access the interrupt handler variables. */
660         ReleaseMutex( pvInterruptEventMutex );
661     }
662 }
663 /*-----------------------------------------------------------*/
664 
vPortSetInterruptHandler(uint32_t ulInterruptNumber,uint32_t (* pvHandler)(void))665 void vPortSetInterruptHandler( uint32_t ulInterruptNumber,
666                                uint32_t ( * pvHandler )( void ) )
667 {
668     if( ulInterruptNumber < portMAX_INTERRUPTS )
669     {
670         if( pvInterruptEventMutex != NULL )
671         {
672             WaitForSingleObject( pvInterruptEventMutex, INFINITE );
673             ulIsrHandler[ ulInterruptNumber ] = pvHandler;
674             ReleaseMutex( pvInterruptEventMutex );
675         }
676         else
677         {
678             ulIsrHandler[ ulInterruptNumber ] = pvHandler;
679         }
680     }
681 }
682 /*-----------------------------------------------------------*/
683 
vPortEnterCritical(void)684 void vPortEnterCritical( void )
685 {
686     if( xPortRunning == pdTRUE )
687     {
688         /* The interrupt event mutex is held for the entire critical section,
689          * effectively disabling (simulated) interrupts. */
690         WaitForSingleObject( pvInterruptEventMutex, INFINITE );
691     }
692 
693     ulCriticalNesting++;
694 }
695 /*-----------------------------------------------------------*/
696 
vPortExitCritical(void)697 void vPortExitCritical( void )
698 {
699     int32_t lMutexNeedsReleasing;
700 
701     /* The interrupt event mutex should already be held by this thread as it was
702      * obtained on entry to the critical section. */
703     lMutexNeedsReleasing = pdTRUE;
704 
705     if( ulCriticalNesting > portNO_CRITICAL_NESTING )
706     {
707         ulCriticalNesting--;
708 
709         /* Don't need to wait for any pending interrupts to execute if the
710          * critical section was exited from inside an interrupt. */
711         if( ( ulCriticalNesting == portNO_CRITICAL_NESTING ) && ( xInsideInterrupt == pdFALSE ) )
712         {
713             /* Were any interrupts set to pending while interrupts were
714              * (simulated) disabled? */
715             if( ulPendingInterrupts != 0UL )
716             {
717                 ThreadState_t * pxThreadState = ( ThreadState_t * ) *( ( size_t * ) pxCurrentTCB );
718 
719                 configASSERT( xPortRunning );
720 
721                 /* The interrupt won't actually executed until
722                  * pvInterruptEventMutex is released as it waits on both
723                  * pvInterruptEventMutex and pvInterruptEvent.
724                  * pvInterruptEvent is only set when the simulated
725                  * interrupt is pended if the interrupt is pended
726                  * from outside a critical section - hence it is set
727                  * here. */
728                 SetEvent( pvInterruptEvent );
729 
730                 /* The calling task is going to wait for an event to ensure the
731                  * interrupt that is pending executes immediately after the
732                  * critical section is exited - so make sure the event is not
733                  * already signaled. */
734                 ResetEvent( pxThreadState->pvYieldEvent );
735 
736                 /* Mutex will be released now so the (simulated) interrupt can
737                  * execute, so does not require releasing on function exit. */
738                 lMutexNeedsReleasing = pdFALSE;
739                 ReleaseMutex( pvInterruptEventMutex );
740                 WaitForSingleObject( pxThreadState->pvYieldEvent, INFINITE );
741             }
742         }
743     }
744 
745     if( pvInterruptEventMutex != NULL )
746     {
747         if( lMutexNeedsReleasing == pdTRUE )
748         {
749             configASSERT( xPortRunning );
750             ReleaseMutex( pvInterruptEventMutex );
751         }
752     }
753 }
754 /*-----------------------------------------------------------*/
755