]> git.sur5r.net Git - freertos/blob - Source/portable/MSVC-MingW/port.c
Lowered the thread priorities used by the Win32 port, and added in a method to delete...
[freertos] / Source / portable / MSVC-MingW / port.c
1 /*\r
2     FreeRTOS V6.1.0 - Copyright (C) 2010 Real Time Engineers Ltd.\r
3 \r
4     ***************************************************************************\r
5     *                                                                         *\r
6     * If you are:                                                             *\r
7     *                                                                         *\r
8     *    + New to FreeRTOS,                                                   *\r
9     *    + Wanting to learn FreeRTOS or multitasking in general quickly       *\r
10     *    + Looking for basic training,                                        *\r
11     *    + Wanting to improve your FreeRTOS skills and productivity           *\r
12     *                                                                         *\r
13     * then take a look at the FreeRTOS books - available as PDF or paperback  *\r
14     *                                                                         *\r
15     *        "Using the FreeRTOS Real Time Kernel - a Practical Guide"        *\r
16     *                  http://www.FreeRTOS.org/Documentation                  *\r
17     *                                                                         *\r
18     * A pdf reference manual is also available.  Both are usually delivered   *\r
19     * to your inbox within 20 minutes to two hours when purchased between 8am *\r
20     * and 8pm GMT (although please allow up to 24 hours in case of            *\r
21     * exceptional circumstances).  Thank you for your support!                *\r
22     *                                                                         *\r
23     ***************************************************************************\r
24 \r
25     This file is part of the FreeRTOS distribution.\r
26 \r
27     FreeRTOS is free software; you can redistribute it and/or modify it under\r
28     the terms of the GNU General Public License (version 2) as published by the\r
29     Free Software Foundation AND MODIFIED BY the FreeRTOS exception.\r
30     ***NOTE*** The exception to the GPL is included to allow you to distribute\r
31     a combined work that includes FreeRTOS without being obliged to provide the\r
32     source code for proprietary components outside of the FreeRTOS kernel.\r
33     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT\r
34     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
35     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for\r
36     more details. You should have received a copy of the GNU General Public\r
37     License and the FreeRTOS license exception along with FreeRTOS; if not it\r
38     can be viewed here: http://www.freertos.org/a00114.html and also obtained\r
39     by writing to Richard Barry, contact details for whom are available on the\r
40     FreeRTOS WEB site.\r
41 \r
42     1 tab == 4 spaces!\r
43 \r
44     http://www.FreeRTOS.org - Documentation, latest information, license and\r
45     contact details.\r
46 \r
47     http://www.SafeRTOS.com - A version that is certified for use in safety\r
48     critical systems.\r
49 \r
50     http://www.OpenRTOS.com - Commercial support, development, porting,\r
51     licensing and training services.\r
52 */\r
53 \r
54 /* Scheduler includes. */\r
55 #include "FreeRTOS.h"\r
56 #include "task.h"\r
57 #include <stdio.h>\r
58 \r
59 #define portMAX_INTERRUPTS                              ( ( unsigned long ) sizeof( unsigned long ) * 8UL ) /* The number of bits in an unsigned long. */\r
60 #define portNO_CRITICAL_NESTING                 ( ( unsigned long ) 0 )\r
61 \r
62 /*\r
63  * Created as a high priority thread, this function uses a timer to simulate\r
64  * a tick interrupt being generated on an embedded target.  In this Windows\r
65  * environment the timer does not achieve anything approaching real time \r
66  * performance though.\r
67  */\r
68 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter );\r
69 \r
70 /* \r
71  * Process all the simulated interrupts - each represented by a bit in \r
72  * ulPendingInterrupts variable.\r
73  */\r
74 static void prvProcessPseudoInterrupts( void );\r
75 \r
76 /*-----------------------------------------------------------*/\r
77 \r
78 /* The WIN32 simulator runs each task in a thread.  The context switching is\r
79 managed by the threads, so the task stack does not have to be managed directly,\r
80 although the task stack is still used to hold an xThreadState structure this is\r
81 the only thing it will ever hold.  The structure indirectly maps the task handle \r
82 to a thread handle. */\r
83 typedef struct\r
84 {\r
85         /* Handle of the thread that executes the task. */\r
86         void *pvThread;\r
87 \r
88 } xThreadState;\r
89 \r
90 /* Pseudo interrupts waiting to be processed.  This is a bit mask where each\r
91 bit represents one interrupt, so a maximum of 32 interrupts can be simulated. */\r
92 static volatile unsigned long ulPendingInterrupts = 0UL;\r
93 \r
94 /* An event used to inform the pseudo interrupt processing thread (a high \r
95 priority thread that simulated interrupt processing) that an interrupt is\r
96 pending. */\r
97 static void *pvInterruptEvent = NULL;\r
98 \r
99 /* Mutex used to protect all the pseudo interrupt variables that are accessed \r
100 by multiple threads. */\r
101 static void *pvInterruptEventMutex = NULL;\r
102 \r
103 /* Events used to manage sequencing. */\r
104 static void *pvTickAcknowledgeEvent = NULL;\r
105 \r
106 /* The critical nesting count for the currently executing task.  This is \r
107 initialised to a non-zero value so interrupts do not become enabled during \r
108 the initialisation phase.  As each task has its own critical nesting value \r
109 ulCriticalNesting will get set to zero when the first task runs.  This \r
110 initialisation is probably not critical in this simulated environment as the\r
111 pseudo interrupt handlers do not get created until the FreeRTOS scheduler is \r
112 started anyway. */\r
113 static unsigned long ulCriticalNesting = 9999UL;\r
114 \r
115 /* Handlers for all the simulated software interrupts.  The first two positions\r
116 are used for the Yield and Tick interrupts so are handled slightly differently,\r
117 all the other interrupts can be user defined. */\r
118 static void (*vIsrHandler[ portMAX_INTERRUPTS ])( void ) = { 0 };\r
119 \r
120 /* Pointer to the TCB of the currently executing task. */\r
121 extern void *pxCurrentTCB;\r
122 \r
123 /*-----------------------------------------------------------*/\r
124 \r
125 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter )\r
126 {\r
127         /* Just to prevent compiler warnings. */\r
128         ( void ) lpParameter;\r
129 \r
130         for(;;)\r
131         {\r
132                 /* Wait until the timer expires and we can access the pseudo interrupt \r
133                 variables.  *NOTE* this is not a 'real time' way of generating tick \r
134                 events as the next wake time should be relative to the previous wake \r
135                 time, not the time that Sleep() is called.  It is done this way to \r
136                 prevent overruns in this very non real time simulated/emulated \r
137                 environment. */\r
138                 Sleep( portTICK_RATE_MS );\r
139 \r
140                 WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
141 \r
142                 /* The timer has expired, generate the simulated tick event. */\r
143                 ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );\r
144 \r
145                 /* The interrupt is now pending - notify the simulated interrupt \r
146                 handler thread. */\r
147                 SetEvent( pvInterruptEvent );\r
148 \r
149                 /* Give back the mutex so the pseudo interrupt handler unblocks \r
150                 and can access the interrupt handler variables.  This high priority\r
151                 task will then loop back round after waiting for the lower priority \r
152                 pseudo interrupt handler thread to acknowledge the tick. */\r
153                 SignalObjectAndWait( pvInterruptEventMutex, pvTickAcknowledgeEvent, INFINITE, FALSE );\r
154         }\r
155 \r
156         #ifdef __GNUC__\r
157                 /* Should never reach here - MingW complains if you leave this line out,\r
158                 MSVC complains if you put it in. */\r
159                 return 0;\r
160         #endif\r
161 }\r
162 /*-----------------------------------------------------------*/\r
163 \r
164 portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )\r
165 {\r
166 xThreadState *pxThreadState = NULL;\r
167 \r
168         /* In this simulated case a stack is not initialised, but instead a thread\r
169         is created that will execute the task being created.  The thread handles\r
170         the context switching itself.  The xThreadState object is placed onto\r
171         the stack that was created for the task - so the stack buffer is still\r
172         used, just not in the conventional way.  It will not be used for anything\r
173         other than holding this structure. */\r
174         pxThreadState = ( xThreadState * ) ( pxTopOfStack - sizeof( xThreadState ) );\r
175 \r
176         /* Create the thread itself. */\r
177         pxThreadState->pvThread = CreateThread( NULL, 0, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED, NULL );\r
178         SetThreadAffinityMask( pxThreadState->pvThread, 0x01 );\r
179         SetThreadPriorityBoost( pxThreadState->pvThread, TRUE );\r
180         SetThreadPriority( pxThreadState->pvThread, THREAD_PRIORITY_IDLE );\r
181         \r
182         return ( portSTACK_TYPE * ) pxThreadState;\r
183 }\r
184 /*-----------------------------------------------------------*/\r
185 \r
186 portBASE_TYPE xPortStartScheduler( void )\r
187 {\r
188 void *pvHandle;\r
189 long lSuccess = pdPASS;\r
190 xThreadState *pxThreadState;\r
191 \r
192         /* Create the events and mutexes that are used to synchronise all the\r
193         threads. */\r
194         pvInterruptEventMutex = CreateMutex( NULL, FALSE, NULL );\r
195         pvInterruptEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
196         pvTickAcknowledgeEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
197 \r
198         if( ( pvInterruptEventMutex == NULL ) || ( pvInterruptEvent == NULL ) || ( pvTickAcknowledgeEvent == NULL ) )\r
199         {\r
200                 lSuccess = pdFAIL;\r
201         }\r
202 \r
203         /* Set the priority of this thread such that it is above the priority of \r
204         the threads that run tasks.  This higher priority is required to ensure\r
205         pseudo interrupts take priority over tasks. */\r
206         pvHandle = GetCurrentThread();\r
207         if( pvHandle == NULL )\r
208         {\r
209                 lSuccess = pdFAIL;\r
210         }\r
211         \r
212         if( lSuccess == pdPASS )\r
213         {\r
214                 if( SetThreadPriority( pvHandle, THREAD_PRIORITY_BELOW_NORMAL ) == 0 )\r
215                 {\r
216                         lSuccess = pdFAIL;\r
217                 }\r
218                 SetThreadPriorityBoost( pvHandle, TRUE );\r
219                 SetThreadAffinityMask( pvHandle, 0x01 );\r
220         }\r
221 \r
222         if( lSuccess == pdPASS )\r
223         {\r
224                 /* Start the thread that simulates the timer peripheral to generate\r
225                 tick interrupts. */\r
226                 pvHandle = CreateThread( NULL, 0, prvSimulatedPeripheralTimer, NULL, 0, NULL );\r
227                 if( pvHandle != NULL )\r
228                 {\r
229                         SetThreadPriority( pvHandle, THREAD_PRIORITY_NORMAL );\r
230                         SetThreadPriorityBoost( pvHandle, TRUE );\r
231                         SetThreadAffinityMask( pvHandle, 0x01 );\r
232                 }\r
233                 \r
234                 /* Start the highest priority task by obtaining its associated thread \r
235                 state structure, in which is stored the thread handle. */\r
236                 pxThreadState = ( xThreadState * ) *( ( unsigned long * ) pxCurrentTCB );\r
237                 ulCriticalNesting = portNO_CRITICAL_NESTING;\r
238 \r
239                 /* Bump up the priority of the thread that is going to run, in the\r
240                 hope that this will asist in getting the Windows thread scheduler to\r
241                 behave as an embedded engineer might expect. */\r
242                 ResumeThread( pxThreadState->pvThread );\r
243 \r
244                 /* Handle all pseudo interrupts - including yield requests and \r
245                 simulated ticks. */\r
246                 prvProcessPseudoInterrupts();\r
247         }       \r
248         \r
249         /* Would not expect to return from prvProcessPseudoInterrupts(), so should \r
250         not get here. */\r
251         return 0;\r
252 }\r
253 /*-----------------------------------------------------------*/\r
254 \r
255 static void prvProcessPseudoInterrupts( void )\r
256 {\r
257 long lSwitchRequired, lCurrentTaskBeingDeleted;\r
258 xThreadState *pxThreadState;\r
259 void *pvObjectList[ 2 ];\r
260 unsigned long i;\r
261 \r
262         /* Going to block on the mutex that ensured exclusive access to the pseudo \r
263         interrupt objects, and the event that signals that a pseudo interrupt\r
264         should be processed. */\r
265         pvObjectList[ 0 ] = pvInterruptEventMutex;\r
266         pvObjectList[ 1 ] = pvInterruptEvent;\r
267 \r
268         for(;;)\r
269         {\r
270                 WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, INFINITE );\r
271 \r
272                 /* Used to indicate whether the pseudo interrupt processing has\r
273                 necessitated a context switch to another task/thread. */\r
274                 lSwitchRequired = pdFALSE;\r
275                 lCurrentTaskBeingDeleted = pdFALSE;\r
276 \r
277                 /* For each interrupt we are interested in processing, each of which is\r
278                 represented by a bit in the 32bit ulPendingInterrupts variable. */\r
279                 for( i = 0; i < portMAX_INTERRUPTS; i++ )\r
280                 {\r
281                         /* Is the pseudo interrupt pending? */\r
282                         if( ulPendingInterrupts & ( 1UL << i ) )\r
283                         {\r
284                                 switch( i )\r
285                                 {\r
286                                         case portINTERRUPT_YIELD:\r
287 \r
288                                                 lSwitchRequired = pdTRUE;\r
289 \r
290                                                 /* Clear the interrupt pending bit. */\r
291                                                 ulPendingInterrupts &= ~( 1UL << portINTERRUPT_YIELD );\r
292                                                 break;\r
293 \r
294                                         case portINTERRUPT_TICK:\r
295                                         \r
296                                                 /* Process the tick itself. */\r
297                                                 vTaskIncrementTick();\r
298                                                 #if( configUSE_PREEMPTION != 0 )\r
299                                                 {\r
300                                                         /* A context switch is only automatically \r
301                                                         performed from the tick interrupt if the \r
302                                                         pre-emptive scheduler is being used. */\r
303                                                         lSwitchRequired = pdTRUE;\r
304                                                 }\r
305                                                 #endif\r
306                                                         \r
307                                                 /* Clear the interrupt pending bit. */\r
308                                                 ulPendingInterrupts &= ~( 1UL << portINTERRUPT_TICK );\r
309                                                 SetEvent( pvTickAcknowledgeEvent );\r
310                                                 break;\r
311 \r
312                                         case portINTERRUPT_DELETE_THREAD:\r
313 \r
314                                                 lCurrentTaskBeingDeleted = pdTRUE;\r
315 \r
316                                                 /* Clear the interrupt pending bit. */\r
317                                                 ulPendingInterrupts &= ~( 1UL << portINTERRUPT_DELETE_THREAD );\r
318                                                 break;\r
319 \r
320                                         default:\r
321 \r
322                                                 /* Is a handler installed? */\r
323                                                 if( vIsrHandler[ i ] != NULL )\r
324                                                 {\r
325                                                         lSwitchRequired = pdTRUE;\r
326 \r
327                                                         /* Run the actual handler. */\r
328                                                         vIsrHandler[ i ]();\r
329 \r
330                                                         /* Clear the interrupt pending bit. */\r
331                                                         ulPendingInterrupts &= ~( 1UL << i );\r
332 \r
333                                                         /* TODO:  Need to have some sort of handshake \r
334                                                         event here for non-tick and none yield \r
335                                                         interrupts. */\r
336                                                 }\r
337                                                 break;\r
338                                 }\r
339                         }\r
340                 }\r
341 \r
342                 if( ( lSwitchRequired != pdFALSE ) || ( lCurrentTaskBeingDeleted != pdFALSE ) )\r
343                 {\r
344                         void *pvOldCurrentTCB;\r
345 \r
346                         pvOldCurrentTCB = pxCurrentTCB;\r
347 \r
348                         /* Select the next task to run. */\r
349                         vTaskSwitchContext();\r
350 \r
351                         /* If the task selected to enter the running state is not the task\r
352                         that is already in the running state. */\r
353                         if( pvOldCurrentTCB != pxCurrentTCB )\r
354                         {\r
355                                 /* Suspend the old thread. */\r
356                                 pxThreadState = ( xThreadState *) *( ( unsigned long * ) pvOldCurrentTCB );\r
357 \r
358                                 if( lCurrentTaskBeingDeleted != pdFALSE )\r
359                                 {\r
360                                         TerminateThread( pxThreadState->pvThread, 0 );\r
361                                 }\r
362                                 else\r
363                                 {\r
364                                         SuspendThread( pxThreadState->pvThread );\r
365                                 }                                                       \r
366 \r
367                                 /* Obtain the state of the task now selected to enter the \r
368                                 Running state. */\r
369                                 pxThreadState = ( xThreadState * ) ( *( unsigned long *) pxCurrentTCB );\r
370                                 ResumeThread( pxThreadState->pvThread );\r
371                         }\r
372                 }\r
373 \r
374                 ReleaseMutex( pvInterruptEventMutex );\r
375         }\r
376 }\r
377 /*-----------------------------------------------------------*/\r
378 \r
379 void vPortDeleteThread( void *pvTaskToDelete )\r
380 {\r
381 xThreadState *pxThreadState;\r
382 \r
383         if( pvTaskToDelete == pxCurrentTCB )\r
384         {\r
385                 /* The task is deleting itself, and so the thread that is running now\r
386                 is also to be deleted.  This has to be deferred until this thread is\r
387                 no longer running, so its done in the pseudo interrupt handler thread. */\r
388                 vPortGeneratePseudoInterrupt( portINTERRUPT_DELETE_THREAD );\r
389         }\r
390         else\r
391         {\r
392                 WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
393 \r
394                 /* Find the handle of the thread being deleted. */\r
395                 pxThreadState = ( xThreadState * ) ( *( unsigned long *) pvTaskToDelete );\r
396                 TerminateThread( pxThreadState->pvThread, 0 );\r
397 \r
398                 ReleaseMutex( pvInterruptEventMutex );\r
399         }\r
400 }\r
401 /*-----------------------------------------------------------*/\r
402 \r
403 void vPortEndScheduler( void )\r
404 {\r
405         /* This function IS NOT TESTED! */\r
406         TerminateProcess( GetCurrentProcess(), 0 );\r
407 }\r
408 /*-----------------------------------------------------------*/\r
409 \r
410 void vPortGeneratePseudoInterrupt( unsigned long ulInterruptNumber )\r
411 {\r
412 xThreadState *pxThreadState;\r
413 \r
414         if( ( ulInterruptNumber < portMAX_INTERRUPTS ) && ( pvInterruptEventMutex != NULL ) )\r
415         {\r
416                 /* Yield interrupts are processed even when critical nesting is non-zero. */\r
417                 WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
418                 ulPendingInterrupts |= ( 1 << ulInterruptNumber );\r
419 \r
420                 /* The pseudo interrupt is now held pending, but don't actually process it\r
421                 yet if this call is within a critical section.  It is possible for this to\r
422                 be in a critical section as calls to wait for mutexes are accumulative. */\r
423                 if( ulCriticalNesting == 0 )\r
424                 {\r
425                         /* The event handler needs to know to signal the interrupt acknowledge event\r
426                         the next time this task runs. */\r
427                         pxThreadState = ( xThreadState * ) *( ( unsigned long * ) pxCurrentTCB );\r
428                         SetEvent( pvInterruptEvent );                   \r
429                 }\r
430 \r
431                 ReleaseMutex( pvInterruptEventMutex );\r
432         }\r
433 }\r
434 /*-----------------------------------------------------------*/\r
435 \r
436 void vPortSetInterruptHandler( unsigned long ulInterruptNumber, void (*pvHandler)( void ) )\r
437 {\r
438         if( ulInterruptNumber < portMAX_INTERRUPTS )\r
439         {\r
440                 if( pvInterruptEventMutex != NULL )\r
441                 {\r
442                         WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
443                         vIsrHandler[ ulInterruptNumber ] = pvHandler;\r
444                         ReleaseMutex( pvInterruptEventMutex );\r
445                 }\r
446                 else\r
447                 {\r
448                         vIsrHandler[ ulInterruptNumber ] = pvHandler;\r
449                 }\r
450         }\r
451 }\r
452 /*-----------------------------------------------------------*/\r
453 \r
454 void vPortEnterCritical( void )\r
455 {\r
456         if( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED )\r
457         {\r
458                 /* The interrupt event mutex is held for the entire critical section,\r
459                 effectively disabling (pseudo) interrupts. */\r
460                 WaitForSingleObject( pvInterruptEventMutex, INFINITE );\r
461                 ulCriticalNesting++;\r
462         }\r
463         else\r
464         {\r
465                 ulCriticalNesting++;\r
466         }       \r
467 }\r
468 /*-----------------------------------------------------------*/\r
469 \r
470 void vPortExitCritical( void )\r
471 {\r
472 xThreadState *pxThreadState;\r
473 long lMutexNeedsReleasing;\r
474 \r
475         /* The interrupt event mutex should already be held by this thread as it was\r
476         obtained on entry to the critical section. */\r
477 \r
478         lMutexNeedsReleasing = pdTRUE;\r
479 \r
480         if( ulCriticalNesting > portNO_CRITICAL_NESTING )\r
481         {\r
482                 if( ulCriticalNesting == ( portNO_CRITICAL_NESTING + 1 ) )\r
483                 {\r
484                         ulCriticalNesting--;\r
485 \r
486                         /* Were any interrupts set to pending while interrupts were \r
487                         (pseudo) disabled? */\r
488                         if( ulPendingInterrupts != 0UL )\r
489                         {\r
490                                 SetEvent( pvInterruptEvent );\r
491 \r
492                                 /* The event handler needs to know to signal the interrupt \r
493                                 acknowledge event the next time this task runs. */\r
494                                 pxThreadState = ( xThreadState * ) *( ( unsigned long * ) pxCurrentTCB );\r
495 \r
496                                 /* Mutex will be released now, so does not require releasing\r
497                                 on function exit. */\r
498                                 lMutexNeedsReleasing = pdFALSE;\r
499                                 ReleaseMutex( pvInterruptEventMutex );\r
500                         }\r
501                 }\r
502                 else\r
503                 {\r
504                         /* Tick interrupts will still not be processed as the critical\r
505                         nesting depth will not be zero. */\r
506                         ulCriticalNesting--;\r
507                 }\r
508         }\r
509 \r
510         if( lMutexNeedsReleasing == pdTRUE )\r
511         {\r
512                 ReleaseMutex( pvInterruptEventMutex );\r
513         }\r
514 }\r
515 /*-----------------------------------------------------------*/\r
516 \r