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 * https://www.FreeRTOS.org
23 * https://github.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 * signaling the condition variable and then waiting on a condition variable
37 * with the current thread.
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 * stdio (printf() and friends) should be called from a single task
48 * only or serialized with a FreeRTOS primitive such as a binary
50 *----------------------------------------------------------*/
59 #include <sys/times.h>
62 /* 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 /* Restore original signal mask. */
201 (void)pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask, NULL );
205 /*-----------------------------------------------------------*/
207 void vPortEndScheduler( void )
209 struct itimerval itimer;
210 struct sigaction sigtick;
211 Thread_t *xCurrentThread;
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;
218 itimer.it_interval.tv_sec = 0;
219 itimer.it_interval.tv_usec = 0;
220 (void)setitimer( ITIMER_REAL, &itimer, NULL );
222 sigtick.sa_flags = 0;
223 sigtick.sa_handler = SIG_IGN;
224 sigemptyset( &sigtick.sa_mask );
225 sigaction( SIGALRM, &sigtick, NULL );
227 /* Signal the scheduler to exit its loop. */
228 xSchedulerEnd = pdTRUE;
229 (void)pthread_kill( hMainThread, SIG_RESUME );
231 xCurrentThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
232 prvSuspendSelf(xCurrentThread);
234 /*-----------------------------------------------------------*/
236 void vPortEnterCritical( void )
238 if ( uxCriticalNesting == 0 )
240 vPortDisableInterrupts();
244 /*-----------------------------------------------------------*/
246 void vPortExitCritical( void )
250 /* If we have reached 0 then re-enable the interrupts. */
251 if( uxCriticalNesting == 0 )
253 vPortEnableInterrupts();
256 /*-----------------------------------------------------------*/
258 void vPortYieldFromISR( void )
260 Thread_t *xThreadToSuspend;
261 Thread_t *xThreadToResume;
263 xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
265 vTaskSwitchContext();
267 xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
269 prvSwitchThread( xThreadToResume, xThreadToSuspend );
271 /*-----------------------------------------------------------*/
273 void vPortYield( void )
275 vPortEnterCritical();
281 /*-----------------------------------------------------------*/
283 void vPortDisableInterrupts( void )
285 pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL );
287 /*-----------------------------------------------------------*/
289 void vPortEnableInterrupts( void )
291 pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL );
293 /*-----------------------------------------------------------*/
295 portBASE_TYPE xPortSetInterruptMask( void )
297 /* Interrupts are always disabled inside ISRs (signals
301 /*-----------------------------------------------------------*/
303 void vPortClearInterruptMask( portBASE_TYPE xMask )
306 /*-----------------------------------------------------------*/
308 static uint64_t prvGetTimeNs(void)
312 clock_gettime(CLOCK_MONOTONIC, &t);
314 return t.tv_sec * 1000000000ull + t.tv_nsec;
317 static uint64_t prvStartTimeNs;
318 static uint64_t prvTickCount;
321 * Setup the systick timer to generate the tick interrupts at the required
324 void prvSetupTimerInterrupt( void )
326 struct itimerval itimer;
329 /* Initialise the structure with the current timer information. */
330 iRet = getitimer( ITIMER_REAL, &itimer );
333 prvFatalError( "getitimer", errno );
336 /* Set the interval between timer events. */
337 itimer.it_interval.tv_sec = 0;
338 itimer.it_interval.tv_usec = portTICK_RATE_MICROSECONDS;
340 /* Set the current count-down. */
341 itimer.it_value.tv_sec = 0;
342 itimer.it_value.tv_usec = portTICK_RATE_MICROSECONDS;
344 /* Set-up the timer interrupt. */
345 iRet = setitimer( ITIMER_REAL, &itimer, NULL );
348 prvFatalError( "setitimer", errno );
351 prvStartTimeNs = prvGetTimeNs();
353 /*-----------------------------------------------------------*/
355 static void vPortSystemTickHandler( int sig )
357 Thread_t *pxThreadToSuspend;
358 Thread_t *pxThreadToResume;
359 uint64_t xExpectedTicks;
361 uxCriticalNesting++; /* Signals are blocked in this signal handler. */
363 #if ( configUSE_PREEMPTION == 1 )
364 pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
367 /* Tick Increment, accounting for any lost signals or drift in
370 * Comment code to adjust timing according to full demo requirements
371 * xExpectedTicks = (prvGetTimeNs() - prvStartTimeNs)
372 * / (portTICK_RATE_MICROSECONDS * 1000);
374 xTaskIncrementTick();
376 * } while (prvTickCount < xExpectedTicks);
379 #if ( configUSE_PREEMPTION == 1 )
380 /* Select Next Task. */
381 vTaskSwitchContext();
383 pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
385 prvSwitchThread(pxThreadToResume, pxThreadToSuspend);
390 /*-----------------------------------------------------------*/
392 void vPortThreadDying( void *pxTaskToDelete, volatile BaseType_t *pxPendYield )
394 Thread_t *pxThread = prvGetThreadFromTask( pxTaskToDelete );
396 pxThread->xDying = pdTRUE;
399 void vPortCancelThread( void *pxTaskToDelete )
401 Thread_t *pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete );
404 * The thread has already been suspended so it can be safely cancelled.
406 pthread_cancel( pxThreadToCancel->pthread );
407 pthread_join( pxThreadToCancel->pthread, NULL );
409 /*-----------------------------------------------------------*/
411 static void *prvWaitForStart( void * pvParams )
413 Thread_t *pxThread = pvParams;
415 prvSuspendSelf(pxThread);
417 /* Resumed for the first time, unblocks all signals. */
418 uxCriticalNesting = 0;
419 vPortEnableInterrupts();
421 /* Call the task's entry point. */
422 pxThread->pxCode( pxThread->pvParams );
426 /*-----------------------------------------------------------*/
428 static void prvSwitchThread( Thread_t *pxThreadToResume,
429 Thread_t *pxThreadToSuspend )
431 BaseType_t uxSavedCriticalNesting;
433 if ( pxThreadToSuspend != pxThreadToResume )
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.
442 uxSavedCriticalNesting = uxCriticalNesting;
444 prvResumeThread( pxThreadToResume );
445 if ( pxThreadToSuspend->xDying )
447 event_delete(pxThreadToSuspend->ev);
448 pthread_exit( NULL );
450 prvSuspendSelf( pxThreadToSuspend );
452 uxCriticalNesting = uxSavedCriticalNesting;
455 /*-----------------------------------------------------------*/
457 static void prvSuspendSelf( Thread_t *thread )
462 * Suspend this thread by waiting for a pthread_cond_signal event.
464 * A suspended thread must not handle signals (interrupts) so
465 * all signals must be blocked by calling this from:
467 * - Inside a critical section (vPortEnterCritical() /
468 * vPortExitCritical()).
470 * - From a signal handler that has all signals masked.
472 * - A thread with all signals blocked with pthread_sigmask().
474 event_wait(thread->ev);
477 /*-----------------------------------------------------------*/
479 static void prvResumeThread( Thread_t *xThreadId )
481 if ( pthread_self() != xThreadId->pthread )
483 event_signal(xThreadId->ev);
486 /*-----------------------------------------------------------*/
488 static void prvSetupSignalsAndSchedulerPolicy( void )
490 struct sigaction sigresume, sigtick;
493 hMainThread = pthread_self();
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 );
504 * Block all signals in this thread so all new threads
505 * inherits this mask.
507 * When a thread is resumed for the first time, all signals
510 (void)pthread_sigmask( SIG_SETMASK, &xAllSignals,
511 &xSchedulerOriginalSignalMask );
513 /* SIG_RESUME is only used with sigwait() so doesn't need a
515 sigresume.sa_flags = 0;
516 sigresume.sa_handler = SIG_IGN;
517 sigfillset( &sigresume.sa_mask );
519 sigtick.sa_flags = 0;
520 sigtick.sa_handler = vPortSystemTickHandler;
521 sigfillset( &sigtick.sa_mask );
523 iRet = sigaction( SIG_RESUME, &sigresume, NULL );
526 prvFatalError( "sigaction", errno );
529 iRet = sigaction( SIGALRM, &sigtick, NULL );
532 prvFatalError( "sigaction", errno );
535 /*-----------------------------------------------------------*/
537 unsigned long ulPortGetRunTime( void )
543 return ( unsigned long ) xTimes.tms_utime;
545 /*-----------------------------------------------------------*/