2 * FreeRTOS Kernel V10.3.0
3 * Copyright (C) 2020 Cambridge Consultants Ltd.
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 * http://www.FreeRTOS.org
23 * http://aws.amazon.com/freertos
28 /*-----------------------------------------------------------
29 * Implementation of functions defined in portable.h for the Posix port.
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().
35 * Task switch is done by resuming the thread for the next task by
36 * sending it the resume signal (SIGUSR1) and then suspending the
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.
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.
47 * Replacement malloc(), free(), calloc(), and realloc() are provided
48 * for glibc (see below for more information).
50 * stdio (printf() and friends) should be called from a single task
51 * only or serialized with a FreeRTOS primitive such as a binary
53 *----------------------------------------------------------*/
62 #include <sys/times.h>
65 /* Scheduler includes. */
68 /*-----------------------------------------------------------*/
70 #define SIG_RESUME SIGUSR1
81 * The additional per-thread data is stored at the beginning of the
84 static inline Thread_t *prvGetThreadFromTask(TaskHandle_t xTask)
86 StackType_t *pxTopOfStack = *(StackType_t **)xTask;
88 return (Thread_t *)(pxTopOfStack + 1);
91 /*-----------------------------------------------------------*/
93 static pthread_once_t hSigSetupThread = PTHREAD_ONCE_INIT;
94 static sigset_t xResumeSignals;
95 static sigset_t xAllSignals;
96 static sigset_t xSchedulerOriginalSignalMask;
97 static pthread_t hMainThread = ( pthread_t )NULL;
98 static volatile portBASE_TYPE uxCriticalNesting;
99 /*-----------------------------------------------------------*/
101 static portBASE_TYPE xSchedulerEnd = pdFALSE;
102 /*-----------------------------------------------------------*/
104 static void prvSetupSignalsAndSchedulerPolicy( void );
105 static void prvSetupTimerInterrupt( void );
106 static void *prvWaitForStart( void * pvParams );
107 static void prvSwitchThread( Thread_t *xThreadToResume, Thread_t *xThreadToSuspend );
108 static void prvSuspendSelf( void );
109 static void prvResumeThread( pthread_t xThreadId );
110 static void vPortSystemTickHandler( int sig );
111 static void vPortStartFirstTask( void );
112 /*-----------------------------------------------------------*/
115 * The standard glibc malloc(), free() etc. take an internal lock so
116 * it is not safe to switch tasks while calling them.
118 * Requiring the application use the safe xPortMalloc() and
119 * vPortFree() is not sufficient as malloc() is used internally by
120 * glibc (e.g., by strdup() and the pthread library.)
122 * To further complicate things malloc() and free() may be called
123 * outside of task context during pthread destruction so using
124 * vTaskSuspend() and xTaskResumeAll() cannot be used.
125 * vPortEnterCritical() and vPortExitCritical() cannot be used either
126 * as they use global state for the critical section nesting (this
127 * cannot be fixed by using TLS as pthread destruction needs to free
130 * Explicitly save/disable and restore the signal mask to block the
131 * timer (SIGALRM) and other signals.
134 extern void *__libc_malloc(size_t);
135 extern void __libc_free(void *);
136 extern void *__libc_calloc(size_t, size_t);
137 extern void *__libc_realloc(void *ptr, size_t);
139 void *malloc(size_t size)
141 sigset_t xSavedSignals;
144 pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals );
145 ptr = __libc_malloc( size );
146 pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL );
153 sigset_t xSavedSignals;
155 pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals );
157 pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL );
160 void *calloc(size_t nmemb, size_t size)
162 sigset_t xSavedSignals;
165 pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals );
166 ptr = __libc_calloc( nmemb, size );
167 pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL );
172 void *realloc(void *ptr, size_t size)
174 sigset_t xSavedSignals;
176 pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals );
177 ptr = __libc_realloc( ptr, size );
178 pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL );
183 static void prvFatalError( const char *pcCall, int iErrno )
185 fprintf( stderr, "%s: %s\n", pcCall, strerror( iErrno ) );
190 * See header file for description.
192 portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack,
193 portSTACK_TYPE *pxEndOfStack,
194 pdTASK_CODE pxCode, void *pvParameters )
197 pthread_attr_t xThreadAttributes;
201 (void)pthread_once( &hSigSetupThread, prvSetupSignalsAndSchedulerPolicy );
204 * Store the additional thread data at the start of the stack.
206 thread = (Thread_t *)(pxTopOfStack + 1) - 1;
207 pxTopOfStack = (portSTACK_TYPE *)thread - 1;
208 ulStackSize = (pxTopOfStack + 1 - pxEndOfStack) * sizeof(*pxTopOfStack);
210 thread->pxCode = pxCode;
211 thread->pvParams = pvParameters;
212 thread->xDying = pdFALSE;
214 pthread_attr_init( &xThreadAttributes );
215 pthread_attr_setstack( &xThreadAttributes, pxEndOfStack, ulStackSize );
217 vPortEnterCritical();
219 iRet = pthread_create( &thread->pthread, &xThreadAttributes,
220 prvWaitForStart, thread );
223 prvFatalError( "pthread_create", iRet );
230 /*-----------------------------------------------------------*/
232 void vPortStartFirstTask( void )
234 Thread_t *pxFirstThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
236 /* Start the first task. */
237 prvResumeThread( pxFirstThread->pthread );
239 /*-----------------------------------------------------------*/
242 * See header file for description.
244 portBASE_TYPE xPortStartScheduler( void )
249 hMainThread = pthread_self();
251 /* Start the timer that generates the tick ISR. Interrupts are disabled
253 prvSetupTimerInterrupt();
255 /* Start the first task. */
256 vPortStartFirstTask();
258 /* Wait until signaled by vPortEndScheduler(). */
259 sigemptyset( &xSignals );
260 sigaddset( &xSignals, SIG_RESUME );
262 while ( !xSchedulerEnd )
264 sigwait( &xSignals, &iSignal );
267 /* Restore original signal mask. */
268 (void)pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask, NULL );
272 /*-----------------------------------------------------------*/
274 void vPortEndScheduler( void )
276 struct itimerval itimer;
277 struct sigaction sigtick;
279 /* Stop the timer and ignore any pending SIGALRMs that would end
280 * up running on the main thread when it is resumed. */
281 itimer.it_value.tv_sec = 0;
282 itimer.it_value.tv_usec = 0;
283 (void)setitimer( ITIMER_REAL, &itimer, NULL );
285 sigtick.sa_flags = 0;
286 sigtick.sa_handler = SIG_IGN;
287 sigaction( SIGALRM, &sigtick, NULL );
289 /* Signal the scheduler to exit its loop. */
290 xSchedulerEnd = pdTRUE;
291 (void)pthread_kill( hMainThread, SIG_RESUME );
295 /*-----------------------------------------------------------*/
297 void vPortEnterCritical( void )
299 if ( uxCriticalNesting == 0 )
301 vPortDisableInterrupts();
305 /*-----------------------------------------------------------*/
307 void vPortExitCritical( void )
311 /* If we have reached 0 then re-enable the interrupts. */
312 if( uxCriticalNesting == 0 )
314 vPortEnableInterrupts();
317 /*-----------------------------------------------------------*/
319 void vPortYieldFromISR( void )
321 Thread_t *xThreadToSuspend;
322 Thread_t *xThreadToResume;
324 xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
326 vTaskSwitchContext();
328 xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
330 prvSwitchThread( xThreadToResume, xThreadToSuspend );
332 /*-----------------------------------------------------------*/
334 void vPortYield( void )
336 vPortEnterCritical();
342 /*-----------------------------------------------------------*/
344 void vPortDisableInterrupts( void )
346 pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL );
348 /*-----------------------------------------------------------*/
350 void vPortEnableInterrupts( void )
352 pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL );
354 /*-----------------------------------------------------------*/
356 portBASE_TYPE xPortSetInterruptMask( void )
358 /* Interrupts are always disabled inside ISRs (signals
362 /*-----------------------------------------------------------*/
364 void vPortClearInterruptMask( portBASE_TYPE xMask )
367 /*-----------------------------------------------------------*/
369 static uint64_t prvGetTimeNs(void)
373 clock_gettime(CLOCK_MONOTONIC, &t);
375 return t.tv_sec * 1000000000ull + t.tv_nsec;
378 static uint64_t prvStartTimeNs;
379 static uint64_t prvTickCount;
382 * Setup the systick timer to generate the tick interrupts at the required
385 void prvSetupTimerInterrupt( void )
387 struct itimerval itimer;
390 /* Initialise the structure with the current timer information. */
391 iRet = getitimer( ITIMER_REAL, &itimer );
394 prvFatalError( "getitimer", errno );
397 /* Set the interval between timer events. */
398 itimer.it_interval.tv_sec = 0;
399 itimer.it_interval.tv_usec = portTICK_RATE_MICROSECONDS;
401 /* Set the current count-down. */
402 itimer.it_value.tv_sec = 0;
403 itimer.it_value.tv_usec = portTICK_RATE_MICROSECONDS;
405 /* Set-up the timer interrupt. */
406 iRet = setitimer( ITIMER_REAL, &itimer, NULL );
409 prvFatalError( "setitimer", errno );
412 prvStartTimeNs = prvGetTimeNs();
414 /*-----------------------------------------------------------*/
416 static void vPortSystemTickHandler( int sig )
418 Thread_t *pxThreadToSuspend;
419 Thread_t *pxThreadToResume;
420 uint64_t xExpectedTicks;
422 uxCriticalNesting++; /* Signals are blocked in this signal handler. */
424 pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
426 /* Tick Increment, accounting for any lost signals or drift in
428 xExpectedTicks = (prvGetTimeNs() - prvStartTimeNs)
429 / (portTICK_RATE_MICROSECONDS * 1000);
431 xTaskIncrementTick();
433 } while (prvTickCount < xExpectedTicks);
435 #if ( configUSE_PREEMPTION == 1 )
436 /* Select Next Task. */
437 vTaskSwitchContext();
439 pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
441 prvSwitchThread(pxThreadToResume, pxThreadToSuspend);
446 /*-----------------------------------------------------------*/
448 void vPortThreadDying( void *pxTaskToDelete, volatile BaseType_t *pxPendYield )
450 Thread_t *pxThread = prvGetThreadFromTask( pxTaskToDelete );
452 pxThread->xDying = pdTRUE;
455 void vPortCancelThread( void *pxTaskToDelete )
457 Thread_t *pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete );
460 * The thread has already been suspended so it can be safely
463 pthread_cancel( pxThreadToCancel->pthread );
464 pthread_join( pxThreadToCancel->pthread, NULL );
466 /*-----------------------------------------------------------*/
468 static void *prvWaitForStart( void * pvParams )
470 Thread_t *pxThread = pvParams;
474 /* Resumed for the first time, unblocks all signals. */
475 uxCriticalNesting = 0;
476 vPortEnableInterrupts();
478 /* Call the task's entry point. */
479 pxThread->pxCode( pxThread->pvParams );
483 /*-----------------------------------------------------------*/
485 static void prvSwitchThread( Thread_t *pxThreadToResume,
486 Thread_t *pxThreadToSuspend )
488 BaseType_t uxSavedCriticalNesting;
490 if ( pxThreadToSuspend != pxThreadToResume )
495 * The critical section nesting is per-task, so save it on the
496 * stack of the current (suspending thread), restoring it when
497 * we switch back to this task.
499 uxSavedCriticalNesting = uxCriticalNesting;
501 prvResumeThread( pxThreadToResume->pthread );
502 if ( pxThreadToSuspend->xDying )
504 pthread_exit( NULL );
508 uxCriticalNesting = uxSavedCriticalNesting;
511 /*-----------------------------------------------------------*/
513 static void prvSuspendSelf( void )
518 * Suspend this thread by waiting for a SIG_RESUME signal.
520 * A suspended thread must not handle signals (interrupts) so
521 * all signals must be blocked by calling this from:
523 * - Inside a critical section (vPortEnterCritical() /
524 * vPortExitCritical()).
526 * - From a signal handler that has all signals masked.
528 * - A thread with all signals blocked with pthread_sigmask().
530 sigwait( &xResumeSignals, &iSig );
533 /*-----------------------------------------------------------*/
535 static void prvResumeThread( pthread_t xThreadId )
537 if ( pthread_self() != xThreadId )
539 pthread_kill( xThreadId, SIG_RESUME );
542 /*-----------------------------------------------------------*/
544 static void prvSetupSignalsAndSchedulerPolicy( void )
546 struct sigaction sigresume, sigtick;
549 hMainThread = pthread_self();
551 /* Initialise common signal masks. */
552 sigemptyset( &xResumeSignals );
553 sigaddset( &xResumeSignals, SIG_RESUME );
554 sigfillset( &xAllSignals );
555 /* Don't block SIGINT so this can be used to break into GDB while
556 * in a critical section. */
557 sigdelset( &xAllSignals, SIGINT );
560 * Block all signals in this thread so all new threads
561 * inherits this mask.
563 * When a thread is resumed for the first time, all signals
566 (void)pthread_sigmask( SIG_SETMASK, &xAllSignals,
567 &xSchedulerOriginalSignalMask );
569 /* SIG_RESUME is only used with sigwait() so doesn't need a
571 sigresume.sa_flags = 0;
572 sigresume.sa_handler = SIG_IGN;
573 sigfillset( &sigresume.sa_mask );
575 sigtick.sa_flags = 0;
576 sigtick.sa_handler = vPortSystemTickHandler;
577 sigfillset( &sigtick.sa_mask );
579 iRet = sigaction( SIG_RESUME, &sigresume, NULL );
582 prvFatalError( "sigaction", errno );
585 iRet = sigaction( SIGALRM, &sigtick, NULL );
588 prvFatalError( "sigaction", errno );
591 /*-----------------------------------------------------------*/
593 unsigned long ulPortGetRunTime( void )
599 return ( unsigned long ) xTimes.tms_utime;
601 /*-----------------------------------------------------------*/