]> begriffs open source - freertos/blob - portable/ThirdParty/GCC/Posix/port.c
Fix: Comment - xTaskIncrementTick loop - to adhere to demo requirement (#162)
[freertos] / portable / ThirdParty / GCC / Posix / port.c
1 /*
2  * FreeRTOS Kernel V10.3.0
3  * Copyright (C) 2020 Cambridge Consultants Ltd.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9  * the Software, and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * https://www.FreeRTOS.org
23  * https://github.com/FreeRTOS
24  *
25  * 1 tab == 4 spaces!
26  */
27
28 /*-----------------------------------------------------------
29  * Implementation of functions defined in portable.h for the Posix port.
30  *
31  * Each task has a pthread which eases use of standard debuggers
32  * (allowing backtraces of tasks etc). Threads for tasks that are not
33  * running are blocked in sigwait().
34  *
35  * Task switch is done by resuming the thread for the next task by
36  * signaling the condition variable and then waiting on a condition variable
37  * with the current thread.
38  *
39  * The timer interrupt uses SIGALRM and care is taken to ensure that
40  * the signal handler runs only on the thread for the current task.
41  *
42  * Use of part of the standard C library requires care as some
43  * functions can take pthread mutexes internally which can result in
44  * deadlocks as the FreeRTOS kernel can switch tasks while they're
45  * holding a pthread mutex.
46  *
47  * stdio (printf() and friends) should be called from a single task
48  * only or serialized with a FreeRTOS primitive such as a binary
49  * semaphore or mutex.
50  *----------------------------------------------------------*/
51
52 #include <errno.h>
53 #include <pthread.h>
54 #include <signal.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <sys/time.h>
59 #include <sys/times.h>
60 #include <time.h>
61
62 /* Scheduler includes. */
63 #include "FreeRTOS.h"
64 #include "task.h"
65 #include "utils/wait_for_event.h"
66 /*-----------------------------------------------------------*/
67
68 #define SIG_RESUME SIGUSR1
69
70 typedef struct THREAD
71 {
72         pthread_t pthread;
73         pdTASK_CODE pxCode;
74         void *pvParams;
75         BaseType_t xDying;
76         struct event *ev;
77 } Thread_t;
78
79 /*
80  * The additional per-thread data is stored at the beginning of the
81  * task's stack.
82  */
83 static inline Thread_t *prvGetThreadFromTask(TaskHandle_t xTask)
84 {
85 StackType_t *pxTopOfStack = *(StackType_t **)xTask;
86
87         return (Thread_t *)(pxTopOfStack + 1);
88 }
89
90 /*-----------------------------------------------------------*/
91
92 static pthread_once_t hSigSetupThread = PTHREAD_ONCE_INIT;
93 static sigset_t xResumeSignals;
94 static sigset_t xAllSignals;
95 static sigset_t xSchedulerOriginalSignalMask;
96 static pthread_t hMainThread = ( pthread_t )NULL;
97 static volatile portBASE_TYPE uxCriticalNesting;
98 /*-----------------------------------------------------------*/
99
100 static portBASE_TYPE xSchedulerEnd = pdFALSE;
101 /*-----------------------------------------------------------*/
102
103 static void prvSetupSignalsAndSchedulerPolicy( void );
104 static void prvSetupTimerInterrupt( void );
105 static void *prvWaitForStart( void * pvParams );
106 static void prvSwitchThread( Thread_t * xThreadToResume,
107                              Thread_t *xThreadToSuspend );
108 static void prvSuspendSelf( Thread_t * thread);
109 static void prvResumeThread( Thread_t * xThreadId );
110 static void vPortSystemTickHandler( int sig );
111 static void vPortStartFirstTask( void );
112 /*-----------------------------------------------------------*/
113
114 static void prvFatalError( const char *pcCall, int iErrno )
115 {
116         fprintf( stderr, "%s: %s\n", pcCall, strerror( iErrno ) );
117         abort();
118 }
119
120 /*
121  * See header file for description.
122  */
123 portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack,
124                                        portSTACK_TYPE *pxEndOfStack,
125                                        pdTASK_CODE pxCode, void *pvParameters )
126 {
127 Thread_t *thread;
128 pthread_attr_t xThreadAttributes;
129 size_t ulStackSize;
130 int iRet;
131
132         (void)pthread_once( &hSigSetupThread, prvSetupSignalsAndSchedulerPolicy );
133
134         /*
135          * Store the additional thread data at the start of the stack.
136          */
137         thread = (Thread_t *)(pxTopOfStack + 1) - 1;
138         pxTopOfStack = (portSTACK_TYPE *)thread - 1;
139         ulStackSize = (pxTopOfStack + 1 - pxEndOfStack) * sizeof(*pxTopOfStack);
140
141         thread->pxCode = pxCode;
142         thread->pvParams = pvParameters;
143         thread->xDying = pdFALSE;
144
145         pthread_attr_init( &xThreadAttributes );
146         pthread_attr_setstack( &xThreadAttributes, pxEndOfStack, ulStackSize );
147
148         thread->ev = event_create();
149
150         vPortEnterCritical();
151
152         iRet = pthread_create( &thread->pthread, &xThreadAttributes,
153                                                    prvWaitForStart, thread );
154         if ( iRet )
155         {
156                 prvFatalError( "pthread_create", iRet );
157         }
158
159         vPortExitCritical();
160
161         return pxTopOfStack;
162 }
163 /*-----------------------------------------------------------*/
164
165 void vPortStartFirstTask( void )
166 {
167 Thread_t *pxFirstThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
168
169         /* Start the first task. */
170         prvResumeThread( pxFirstThread );
171 }
172 /*-----------------------------------------------------------*/
173
174 /*
175  * See header file for description.
176  */
177 portBASE_TYPE xPortStartScheduler( void )
178 {
179 int iSignal;
180 sigset_t xSignals;
181
182         hMainThread = pthread_self();
183
184         /* Start the timer that generates the tick ISR(SIGALRM).
185            Interrupts are disabled here already. */
186         prvSetupTimerInterrupt();
187
188         /* Start the first task. */
189         vPortStartFirstTask();
190
191         /* Wait until signaled by vPortEndScheduler(). */
192         sigemptyset( &xSignals );
193         sigaddset( &xSignals, SIG_RESUME );
194
195         while ( !xSchedulerEnd )
196         {
197                 sigwait( &xSignals, &iSignal );
198         }
199
200         /* Restore original signal mask. */
201         (void)pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask,  NULL );
202
203         return 0;
204 }
205 /*-----------------------------------------------------------*/
206
207 void vPortEndScheduler( void )
208 {
209 struct itimerval itimer;
210 struct sigaction sigtick;
211 Thread_t *xCurrentThread;
212
213         /* Stop the timer and ignore any pending SIGALRMs that would end
214          * up running on the main thread when it is resumed. */
215         itimer.it_value.tv_sec = 0;
216         itimer.it_value.tv_usec = 0;
217
218         itimer.it_interval.tv_sec = 0;
219         itimer.it_interval.tv_usec = 0;
220         (void)setitimer( ITIMER_REAL, &itimer, NULL );
221
222         sigtick.sa_flags = 0;
223         sigtick.sa_handler = SIG_IGN;
224         sigemptyset( &sigtick.sa_mask );
225         sigaction( SIGALRM, &sigtick, NULL );
226
227         /* Signal the scheduler to exit its loop. */
228         xSchedulerEnd = pdTRUE;
229         (void)pthread_kill( hMainThread, SIG_RESUME );
230
231         xCurrentThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
232         prvSuspendSelf(xCurrentThread);
233 }
234 /*-----------------------------------------------------------*/
235
236 void vPortEnterCritical( void )
237 {
238         if ( uxCriticalNesting == 0 )
239         {
240                 vPortDisableInterrupts();
241         }
242         uxCriticalNesting++;
243 }
244 /*-----------------------------------------------------------*/
245
246 void vPortExitCritical( void )
247 {
248         uxCriticalNesting--;
249
250         /* If we have reached 0 then re-enable the interrupts. */
251         if( uxCriticalNesting == 0 )
252         {
253                 vPortEnableInterrupts();
254         }
255 }
256 /*-----------------------------------------------------------*/
257
258 void vPortYieldFromISR( void )
259 {
260 Thread_t *xThreadToSuspend;
261 Thread_t *xThreadToResume;
262
263         xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
264
265         vTaskSwitchContext();
266
267         xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
268
269         prvSwitchThread( xThreadToResume, xThreadToSuspend );
270 }
271 /*-----------------------------------------------------------*/
272
273 void vPortYield( void )
274 {
275         vPortEnterCritical();
276
277         vPortYieldFromISR();
278
279         vPortExitCritical();
280 }
281 /*-----------------------------------------------------------*/
282
283 void vPortDisableInterrupts( void )
284 {
285         pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL );
286 }
287 /*-----------------------------------------------------------*/
288
289 void vPortEnableInterrupts( void )
290 {
291         pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL );
292 }
293 /*-----------------------------------------------------------*/
294
295 portBASE_TYPE xPortSetInterruptMask( void )
296 {
297         /* Interrupts are always disabled inside ISRs (signals
298            handlers). */
299         return pdTRUE;
300 }
301 /*-----------------------------------------------------------*/
302
303 void vPortClearInterruptMask( portBASE_TYPE xMask )
304 {
305 }
306 /*-----------------------------------------------------------*/
307
308 static uint64_t prvGetTimeNs(void)
309 {
310 struct timespec t;
311
312         clock_gettime(CLOCK_MONOTONIC, &t);
313
314         return t.tv_sec * 1000000000ull + t.tv_nsec;
315 }
316
317 static uint64_t prvStartTimeNs;
318 static uint64_t prvTickCount;
319
320 /*
321  * Setup the systick timer to generate the tick interrupts at the required
322  * frequency.
323  */
324 void prvSetupTimerInterrupt( void )
325 {
326 struct itimerval itimer;
327 int iRet;
328
329         /* Initialise the structure with the current timer information. */
330         iRet = getitimer( ITIMER_REAL, &itimer );
331         if ( iRet )
332         {
333                 prvFatalError( "getitimer", errno );
334         }
335
336         /* Set the interval between timer events. */
337         itimer.it_interval.tv_sec = 0;
338         itimer.it_interval.tv_usec = portTICK_RATE_MICROSECONDS;
339
340         /* Set the current count-down. */
341         itimer.it_value.tv_sec = 0;
342         itimer.it_value.tv_usec = portTICK_RATE_MICROSECONDS;
343
344         /* Set-up the timer interrupt. */
345         iRet = setitimer( ITIMER_REAL, &itimer, NULL );
346         if ( iRet )
347         {
348                 prvFatalError( "setitimer", errno );
349         }
350
351         prvStartTimeNs = prvGetTimeNs();
352 }
353 /*-----------------------------------------------------------*/
354
355 static void vPortSystemTickHandler( int sig )
356 {
357 Thread_t *pxThreadToSuspend;
358 Thread_t *pxThreadToResume;
359 uint64_t xExpectedTicks;
360
361         uxCriticalNesting++; /* Signals are blocked in this signal handler. */
362
363 #if ( configUSE_PREEMPTION == 1 )
364         pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
365 #endif
366
367         /* Tick Increment, accounting for any lost signals or drift in
368          * the timer. */
369 /*
370  *      Comment code to adjust timing according to full demo requirements
371  *      xExpectedTicks = (prvGetTimeNs() - prvStartTimeNs)
372  *              / (portTICK_RATE_MICROSECONDS * 1000);
373  * do { */
374                 xTaskIncrementTick();
375 /*              prvTickCount++;
376  *      } while (prvTickCount < xExpectedTicks);
377 */
378
379 #if ( configUSE_PREEMPTION == 1 )
380         /* Select Next Task. */
381         vTaskSwitchContext();
382
383         pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
384
385         prvSwitchThread(pxThreadToResume, pxThreadToSuspend);
386 #endif
387
388         uxCriticalNesting--;
389 }
390 /*-----------------------------------------------------------*/
391
392 void vPortThreadDying( void *pxTaskToDelete, volatile BaseType_t *pxPendYield )
393 {
394 Thread_t *pxThread = prvGetThreadFromTask( pxTaskToDelete );
395
396         pxThread->xDying = pdTRUE;
397 }
398
399 void vPortCancelThread( void *pxTaskToDelete )
400 {
401 Thread_t *pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete );
402
403         /*
404          * The thread has already been suspended so it can be safely cancelled.
405          */
406         pthread_cancel( pxThreadToCancel->pthread );
407         pthread_join( pxThreadToCancel->pthread, NULL );
408 }
409 /*-----------------------------------------------------------*/
410
411 static void *prvWaitForStart( void * pvParams )
412 {
413 Thread_t *pxThread = pvParams;
414
415         prvSuspendSelf(pxThread);
416
417         /* Resumed for the first time, unblocks all signals. */
418         uxCriticalNesting = 0;
419         vPortEnableInterrupts();
420
421         /* Call the task's entry point. */
422         pxThread->pxCode( pxThread->pvParams );
423
424         return NULL;
425 }
426 /*-----------------------------------------------------------*/
427
428 static void prvSwitchThread( Thread_t *pxThreadToResume,
429                                                          Thread_t *pxThreadToSuspend )
430 {
431 BaseType_t uxSavedCriticalNesting;
432
433         if ( pxThreadToSuspend != pxThreadToResume )
434         {
435                 /*
436                  * Switch tasks.
437                  *
438                  * The critical section nesting is per-task, so save it on the
439                  * stack of the current (suspending thread), restoring it when
440                  * we switch back to this task.
441                  */
442                 uxSavedCriticalNesting = uxCriticalNesting;
443
444                 prvResumeThread( pxThreadToResume );
445                 if ( pxThreadToSuspend->xDying )
446                 {
447                         event_delete(pxThreadToSuspend->ev);
448                         pthread_exit( NULL );
449                 }
450                 prvSuspendSelf( pxThreadToSuspend );
451
452                 uxCriticalNesting = uxSavedCriticalNesting;
453         }
454 }
455 /*-----------------------------------------------------------*/
456
457 static void prvSuspendSelf( Thread_t *thread )
458 {
459 int iSig;
460
461         /*
462          * Suspend this thread by waiting for a pthread_cond_signal event.
463          *
464          * A suspended thread must not handle signals (interrupts) so
465          * all signals must be blocked by calling this from:
466          *
467          * - Inside a critical section (vPortEnterCritical() /
468          *   vPortExitCritical()).
469          *
470          * - From a signal handler that has all signals masked.
471          *
472          * - A thread with all signals blocked with pthread_sigmask().
473         */
474     event_wait(thread->ev);
475 }
476
477 /*-----------------------------------------------------------*/
478
479 static void prvResumeThread( Thread_t *xThreadId )
480 {
481         if ( pthread_self() != xThreadId->pthread )
482         {
483                 event_signal(xThreadId->ev);
484         }
485 }
486 /*-----------------------------------------------------------*/
487
488 static void prvSetupSignalsAndSchedulerPolicy( void )
489 {
490 struct sigaction sigresume, sigtick;
491 int iRet;
492
493         hMainThread = pthread_self();
494
495         /* Initialise common signal masks. */
496         sigemptyset( &xResumeSignals );
497         sigaddset( &xResumeSignals, SIG_RESUME );
498         sigfillset( &xAllSignals );
499         /* Don't block SIGINT so this can be used to break into GDB while
500          * in a critical section. */
501         sigdelset( &xAllSignals, SIGINT );
502
503         /*
504          * Block all signals in this thread so all new threads
505          * inherits this mask.
506          *
507          * When a thread is resumed for the first time, all signals
508          * will be unblocked.
509          */
510         (void)pthread_sigmask( SIG_SETMASK, &xAllSignals,
511                                                    &xSchedulerOriginalSignalMask );
512
513         /* SIG_RESUME is only used with sigwait() so doesn't need a
514            handler. */
515         sigresume.sa_flags = 0;
516         sigresume.sa_handler = SIG_IGN;
517         sigfillset( &sigresume.sa_mask );
518
519         sigtick.sa_flags = 0;
520         sigtick.sa_handler = vPortSystemTickHandler;
521         sigfillset( &sigtick.sa_mask );
522
523         iRet = sigaction( SIG_RESUME, &sigresume, NULL );
524         if ( iRet )
525         {
526                 prvFatalError( "sigaction", errno );
527         }
528
529         iRet = sigaction( SIGALRM, &sigtick, NULL );
530         if ( iRet )
531         {
532                 prvFatalError( "sigaction", errno );
533         }
534 }
535 /*-----------------------------------------------------------*/
536
537 unsigned long ulPortGetRunTime( void )
538 {
539 struct tms xTimes;
540
541         times( &xTimes );
542
543         return ( unsigned long ) xTimes.tms_utime;
544 }
545 /*-----------------------------------------------------------*/