2 * FreeRTOS Kernel V10.4.3 LTS Patch 3
3 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
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:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
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.
22 * https://www.FreeRTOS.org
23 * https://github.com/FreeRTOS
27 /*-----------------------------------------------------------
28 * Implementation of functions defined in portable.h for the Posix port.
30 * Each task has a pthread which eases use of standard debuggers
31 * (allowing backtraces of tasks etc). Threads for tasks that are not
32 * running are blocked in sigwait().
34 * Task switch is done by resuming the thread for the next task by
35 * signaling the condition variable and then waiting on a condition variable
36 * with the current thread.
38 * The timer interrupt uses SIGALRM and care is taken to ensure that
39 * the signal handler runs only on the thread for the current task.
41 * Use of part of the standard C library requires care as some
42 * functions can take pthread mutexes internally which can result in
43 * deadlocks as the FreeRTOS kernel can switch tasks while they're
44 * holding a pthread mutex.
46 * stdio (printf() and friends) should be called from a single task
47 * only or serialized with a FreeRTOS primitive such as a binary
49 *----------------------------------------------------------*/
58 #include <sys/times.h>
61 /* Scheduler includes. */
65 #include "utils/wait_for_event.h"
66 /*-----------------------------------------------------------*/
68 #define SIG_RESUME SIGUSR1
80 * The additional per-thread data is stored at the beginning of the
83 static inline Thread_t *prvGetThreadFromTask(TaskHandle_t xTask)
85 StackType_t *pxTopOfStack = *(StackType_t **)xTask;
87 return (Thread_t *)(pxTopOfStack + 1);
90 /*-----------------------------------------------------------*/
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 /*-----------------------------------------------------------*/
100 static portBASE_TYPE xSchedulerEnd = pdFALSE;
101 /*-----------------------------------------------------------*/
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 /*-----------------------------------------------------------*/
114 static void prvFatalError( const char *pcCall, int iErrno )
116 fprintf( stderr, "%s: %s\n", pcCall, strerror( iErrno ) );
121 * See header file for description.
123 portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack,
124 portSTACK_TYPE *pxEndOfStack,
125 pdTASK_CODE pxCode, void *pvParameters )
128 pthread_attr_t xThreadAttributes;
132 (void)pthread_once( &hSigSetupThread, prvSetupSignalsAndSchedulerPolicy );
135 * Store the additional thread data at the start of the stack.
137 thread = (Thread_t *)(pxTopOfStack + 1) - 1;
138 pxTopOfStack = (portSTACK_TYPE *)thread - 1;
139 ulStackSize = (pxTopOfStack + 1 - pxEndOfStack) * sizeof(*pxTopOfStack);
141 thread->pxCode = pxCode;
142 thread->pvParams = pvParameters;
143 thread->xDying = pdFALSE;
145 pthread_attr_init( &xThreadAttributes );
146 pthread_attr_setstack( &xThreadAttributes, pxEndOfStack, ulStackSize );
148 thread->ev = event_create();
150 vPortEnterCritical();
152 iRet = pthread_create( &thread->pthread, &xThreadAttributes,
153 prvWaitForStart, thread );
156 prvFatalError( "pthread_create", iRet );
163 /*-----------------------------------------------------------*/
165 void vPortStartFirstTask( void )
167 Thread_t *pxFirstThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
169 /* Start the first task. */
170 prvResumeThread( pxFirstThread );
172 /*-----------------------------------------------------------*/
175 * See header file for description.
177 portBASE_TYPE xPortStartScheduler( void )
182 hMainThread = pthread_self();
184 /* Start the timer that generates the tick ISR(SIGALRM).
185 Interrupts are disabled here already. */
186 prvSetupTimerInterrupt();
188 /* Start the first task. */
189 vPortStartFirstTask();
191 /* Wait until signaled by vPortEndScheduler(). */
192 sigemptyset( &xSignals );
193 sigaddset( &xSignals, SIG_RESUME );
195 while ( !xSchedulerEnd )
197 sigwait( &xSignals, &iSignal );
200 /* Cancel the Idle task and free its resources */
201 #if ( INCLUDE_xTaskGetIdleTaskHandle == 1 )
202 vPortCancelThread( xTaskGetIdleTaskHandle() );
205 #if ( configUSE_TIMERS == 1 )
206 /* Cancel the Timer task and free its resources */
207 vPortCancelThread( xTimerGetTimerDaemonTaskHandle() );
208 #endif /* configUSE_TIMERS */
210 /* Restore original signal mask. */
211 (void)pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask, NULL );
215 /*-----------------------------------------------------------*/
217 void vPortEndScheduler( void )
219 struct itimerval itimer;
220 struct sigaction sigtick;
221 Thread_t *xCurrentThread;
223 /* Stop the timer and ignore any pending SIGALRMs that would end
224 * up running on the main thread when it is resumed. */
225 itimer.it_value.tv_sec = 0;
226 itimer.it_value.tv_usec = 0;
228 itimer.it_interval.tv_sec = 0;
229 itimer.it_interval.tv_usec = 0;
230 (void)setitimer( ITIMER_REAL, &itimer, NULL );
232 sigtick.sa_flags = 0;
233 sigtick.sa_handler = SIG_IGN;
234 sigemptyset( &sigtick.sa_mask );
235 sigaction( SIGALRM, &sigtick, NULL );
237 /* Signal the scheduler to exit its loop. */
238 xSchedulerEnd = pdTRUE;
239 (void)pthread_kill( hMainThread, SIG_RESUME );
241 xCurrentThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
242 prvSuspendSelf(xCurrentThread);
244 /*-----------------------------------------------------------*/
246 void vPortEnterCritical( void )
248 if ( uxCriticalNesting == 0 )
250 vPortDisableInterrupts();
254 /*-----------------------------------------------------------*/
256 void vPortExitCritical( void )
260 /* If we have reached 0 then re-enable the interrupts. */
261 if( uxCriticalNesting == 0 )
263 vPortEnableInterrupts();
266 /*-----------------------------------------------------------*/
268 void vPortYieldFromISR( void )
270 Thread_t *xThreadToSuspend;
271 Thread_t *xThreadToResume;
273 xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
275 vTaskSwitchContext();
277 xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
279 prvSwitchThread( xThreadToResume, xThreadToSuspend );
281 /*-----------------------------------------------------------*/
283 void vPortYield( void )
285 vPortEnterCritical();
291 /*-----------------------------------------------------------*/
293 void vPortDisableInterrupts( void )
295 pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL );
297 /*-----------------------------------------------------------*/
299 void vPortEnableInterrupts( void )
301 pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL );
303 /*-----------------------------------------------------------*/
305 portBASE_TYPE xPortSetInterruptMask( void )
307 /* Interrupts are always disabled inside ISRs (signals
311 /*-----------------------------------------------------------*/
313 void vPortClearInterruptMask( portBASE_TYPE xMask )
316 /*-----------------------------------------------------------*/
318 static uint64_t prvGetTimeNs(void)
322 clock_gettime(CLOCK_MONOTONIC, &t);
324 return t.tv_sec * 1000000000ull + t.tv_nsec;
327 static uint64_t prvStartTimeNs;
328 /* commented as part of the code below in vPortSystemTickHandler,
329 * to adjust timing according to full demo requirements */
330 /* static uint64_t prvTickCount; */
333 * Setup the systick timer to generate the tick interrupts at the required
336 void prvSetupTimerInterrupt( void )
338 struct itimerval itimer;
341 /* Initialise the structure with the current timer information. */
342 iRet = getitimer( ITIMER_REAL, &itimer );
345 prvFatalError( "getitimer", errno );
348 /* Set the interval between timer events. */
349 itimer.it_interval.tv_sec = 0;
350 itimer.it_interval.tv_usec = portTICK_RATE_MICROSECONDS;
352 /* Set the current count-down. */
353 itimer.it_value.tv_sec = 0;
354 itimer.it_value.tv_usec = portTICK_RATE_MICROSECONDS;
356 /* Set-up the timer interrupt. */
357 iRet = setitimer( ITIMER_REAL, &itimer, NULL );
360 prvFatalError( "setitimer", errno );
363 prvStartTimeNs = prvGetTimeNs();
365 /*-----------------------------------------------------------*/
367 static void vPortSystemTickHandler( int sig )
369 Thread_t *pxThreadToSuspend;
370 Thread_t *pxThreadToResume;
371 /* uint64_t xExpectedTicks; */
373 uxCriticalNesting++; /* Signals are blocked in this signal handler. */
375 #if ( configUSE_PREEMPTION == 1 )
376 pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
379 /* Tick Increment, accounting for any lost signals or drift in
382 * Comment code to adjust timing according to full demo requirements
383 * xExpectedTicks = (prvGetTimeNs() - prvStartTimeNs)
384 * / (portTICK_RATE_MICROSECONDS * 1000);
386 xTaskIncrementTick();
388 * } while (prvTickCount < xExpectedTicks);
391 #if ( configUSE_PREEMPTION == 1 )
392 /* Select Next Task. */
393 vTaskSwitchContext();
395 pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
397 prvSwitchThread(pxThreadToResume, pxThreadToSuspend);
402 /*-----------------------------------------------------------*/
404 void vPortThreadDying( void *pxTaskToDelete, volatile BaseType_t *pxPendYield )
406 Thread_t *pxThread = prvGetThreadFromTask( pxTaskToDelete );
408 pxThread->xDying = pdTRUE;
411 void vPortCancelThread( void *pxTaskToDelete )
413 Thread_t *pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete );
416 * The thread has already been suspended so it can be safely cancelled.
418 pthread_cancel( pxThreadToCancel->pthread );
419 pthread_join( pxThreadToCancel->pthread, NULL );
420 event_delete( pxThreadToCancel->ev );
422 /*-----------------------------------------------------------*/
424 static void *prvWaitForStart( void * pvParams )
426 Thread_t *pxThread = pvParams;
428 prvSuspendSelf(pxThread);
430 /* Resumed for the first time, unblocks all signals. */
431 uxCriticalNesting = 0;
432 vPortEnableInterrupts();
434 /* Call the task's entry point. */
435 pxThread->pxCode( pxThread->pvParams );
437 /* A function that implements a task must not exit or attempt to return to
438 * its caller as there is nothing to return to. If a task wants to exit it
439 * should instead call vTaskDelete( NULL ). Artificially force an assert()
440 * to be triggered if configASSERT() is defined, so application writers can
441 * catch the error. */
442 configASSERT( pdFALSE );
446 /*-----------------------------------------------------------*/
448 static void prvSwitchThread( Thread_t *pxThreadToResume,
449 Thread_t *pxThreadToSuspend )
451 BaseType_t uxSavedCriticalNesting;
453 if ( pxThreadToSuspend != pxThreadToResume )
458 * The critical section nesting is per-task, so save it on the
459 * stack of the current (suspending thread), restoring it when
460 * we switch back to this task.
462 uxSavedCriticalNesting = uxCriticalNesting;
464 prvResumeThread( pxThreadToResume );
465 if ( pxThreadToSuspend->xDying )
467 pthread_exit( NULL );
469 prvSuspendSelf( pxThreadToSuspend );
471 uxCriticalNesting = uxSavedCriticalNesting;
474 /*-----------------------------------------------------------*/
476 static void prvSuspendSelf( Thread_t *thread )
479 * Suspend this thread by waiting for a pthread_cond_signal event.
481 * A suspended thread must not handle signals (interrupts) so
482 * all signals must be blocked by calling this from:
484 * - Inside a critical section (vPortEnterCritical() /
485 * vPortExitCritical()).
487 * - From a signal handler that has all signals masked.
489 * - A thread with all signals blocked with pthread_sigmask().
491 event_wait(thread->ev);
494 /*-----------------------------------------------------------*/
496 static void prvResumeThread( Thread_t *xThreadId )
498 if ( pthread_self() != xThreadId->pthread )
500 event_signal(xThreadId->ev);
503 /*-----------------------------------------------------------*/
505 static void prvSetupSignalsAndSchedulerPolicy( void )
507 struct sigaction sigresume, sigtick;
510 hMainThread = pthread_self();
512 /* Initialise common signal masks. */
513 sigemptyset( &xResumeSignals );
514 sigaddset( &xResumeSignals, SIG_RESUME );
515 sigfillset( &xAllSignals );
516 /* Don't block SIGINT so this can be used to break into GDB while
517 * in a critical section. */
518 sigdelset( &xAllSignals, SIGINT );
521 * Block all signals in this thread so all new threads
522 * inherits this mask.
524 * When a thread is resumed for the first time, all signals
527 (void)pthread_sigmask( SIG_SETMASK, &xAllSignals,
528 *&xSchedulerOriginalSignalMask );
530 /* SIG_RESUME is only used with sigwait() so doesn't need a
532 sigresume.sa_flags = 0;
533 sigresume.sa_handler = SIG_IGN;
534 sigfillset( &sigresume.sa_mask );
536 sigtick.sa_flags = 0;
537 sigtick.sa_handler = vPortSystemTickHandler;
538 sigfillset( &sigtick.sa_mask );
540 iRet = sigaction( SIG_RESUME, &sigresume, NULL );
543 prvFatalError( "sigaction", errno );
546 iRet = sigaction( SIGALRM, &sigtick, NULL );
549 prvFatalError( "sigaction", errno );
552 /*-----------------------------------------------------------*/
554 unsigned long ulPortGetRunTime( void )
560 return ( unsigned long ) xTimes.tms_utime;
562 /*-----------------------------------------------------------*/