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