2 * FreeRTOS Kernel <DEVELOPMENT BRANCH>
3 * Copyright (C) 2020 Cambridge Consultants Ltd.
5 * SPDX-License-Identifier: MIT
7 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8 * this software and associated documentation files (the "Software"), to deal in
9 * the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 * the Software, and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 * https://www.FreeRTOS.org
25 * https://github.com/FreeRTOS
29 /*-----------------------------------------------------------
30 * Implementation of functions defined in portable.h for the Posix port.
32 * Each task has a pthread which eases use of standard debuggers
33 * (allowing backtraces of tasks etc). Threads for tasks that are not
34 * running are blocked in sigwait().
36 * Task switch is done by resuming the thread for the next task by
37 * signaling the condition variable and then waiting on a condition variable
38 * with the current thread.
40 * The timer interrupt uses SIGALRM and care is taken to ensure that
41 * the signal handler runs only on the thread for the current task.
43 * Use of part of the standard C library requires care as some
44 * functions can take pthread mutexes internally which can result in
45 * deadlocks as the FreeRTOS kernel can switch tasks while they're
46 * holding a pthread mutex.
48 * stdio (printf() and friends) should be called from a single task
49 * only or serialized with a FreeRTOS primitive such as a binary
51 *----------------------------------------------------------*/
52 #include "portmacro.h"
61 #include <sys/times.h>
65 #include <mach/mach_vm.h>
68 /* Scheduler includes. */
72 #include "utils/wait_for_event.h"
73 /*-----------------------------------------------------------*/
75 #define SIG_RESUME SIGUSR1
80 TaskFunction_t pxCode;
87 * The additional per-thread data is stored at the beginning of the
90 static inline Thread_t * prvGetThreadFromTask( TaskHandle_t xTask )
92 StackType_t * pxTopOfStack = *( StackType_t ** ) xTask;
94 return ( Thread_t * ) ( pxTopOfStack + 1 );
97 /*-----------------------------------------------------------*/
99 static pthread_once_t hSigSetupThread = PTHREAD_ONCE_INIT;
100 static sigset_t xAllSignals;
101 static sigset_t xSchedulerOriginalSignalMask;
102 static pthread_t hMainThread = ( pthread_t ) NULL;
103 static volatile portBASE_TYPE uxCriticalNesting;
104 /*-----------------------------------------------------------*/
106 static portBASE_TYPE xSchedulerEnd = pdFALSE;
107 /*-----------------------------------------------------------*/
109 static void prvSetupSignalsAndSchedulerPolicy( void );
110 static void prvSetupTimerInterrupt( void );
111 static void * prvWaitForStart( void * pvParams );
112 static void prvSwitchThread( Thread_t * xThreadToResume,
113 Thread_t * xThreadToSuspend );
114 static void prvSuspendSelf( Thread_t * thread );
115 static void prvResumeThread( Thread_t * xThreadId );
116 static void vPortSystemTickHandler( int sig );
117 static void vPortStartFirstTask( void );
118 static void prvPortYieldFromISR( void );
119 /*-----------------------------------------------------------*/
121 static void prvFatalError( const char * pcCall,
122 int iErrno ) __attribute__ ((__noreturn__));
124 void prvFatalError( const char * pcCall,
127 fprintf( stderr, "%s: %s\n", pcCall, strerror( iErrno ) );
132 * See header file for description.
134 portSTACK_TYPE * pxPortInitialiseStack( StackType_t * pxTopOfStack,
135 StackType_t * pxEndOfStack,
136 TaskFunction_t pxCode,
137 void * pvParameters )
140 pthread_attr_t xThreadAttributes;
144 ( void ) pthread_once( &hSigSetupThread, prvSetupSignalsAndSchedulerPolicy );
147 * Store the additional thread data at the start of the stack.
149 thread = ( Thread_t * ) ( pxTopOfStack + 1 ) - 1;
150 pxTopOfStack = ( portSTACK_TYPE * ) thread - 1;
151 ulStackSize = ( size_t )( pxTopOfStack + 1 - pxEndOfStack ) * sizeof( *pxTopOfStack );
154 pxEndOfStack = mach_vm_round_page ( pxEndOfStack );
155 ulStackSize = mach_vm_trunc_page ( ulStackSize );
158 thread->pxCode = pxCode;
159 thread->pvParams = pvParameters;
160 thread->xDying = pdFALSE;
162 pthread_attr_init( &xThreadAttributes );
163 iRet = pthread_attr_setstack( &xThreadAttributes, pxEndOfStack, ulStackSize );
166 fprintf( stderr, "[WARN] pthread_attr_setstack failed with return value: %d. Default stack will be used.\n", iRet );
167 fprintf( stderr, "[WARN] Increase the stack size to PTHREAD_STACK_MIN.\n" );
170 thread->ev = event_create();
172 vPortEnterCritical();
174 iRet = pthread_create( &thread->pthread, &xThreadAttributes,
175 prvWaitForStart, thread );
179 prvFatalError( "pthread_create", iRet );
186 /*-----------------------------------------------------------*/
188 void vPortStartFirstTask( void )
190 Thread_t * pxFirstThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
192 /* Start the first task. */
193 prvResumeThread( pxFirstThread );
195 /*-----------------------------------------------------------*/
198 * See header file for description.
200 portBASE_TYPE xPortStartScheduler( void )
205 hMainThread = pthread_self();
207 /* Start the timer that generates the tick ISR(SIGALRM).
208 * Interrupts are disabled here already. */
209 prvSetupTimerInterrupt();
212 * Block SIG_RESUME before starting any tasks so the main thread can sigwait on it.
213 * To sigwait on an unblocked signal is undefined.
214 * https://pubs.opengroup.org/onlinepubs/009604499/functions/sigwait.html
216 sigemptyset( &xSignals );
217 sigaddset( &xSignals, SIG_RESUME );
218 ( void ) pthread_sigmask( SIG_BLOCK, &xSignals, NULL );
220 /* Start the first task. */
221 vPortStartFirstTask();
223 /* Wait until signaled by vPortEndScheduler(). */
224 while( xSchedulerEnd != pdTRUE )
226 sigwait( &xSignals, &iSignal );
229 /* Cancel the Idle task and free its resources */
230 #if ( INCLUDE_xTaskGetIdleTaskHandle == 1 )
231 vPortCancelThread( xTaskGetIdleTaskHandle() );
234 #if ( configUSE_TIMERS == 1 )
235 /* Cancel the Timer task and free its resources */
236 vPortCancelThread( xTimerGetTimerDaemonTaskHandle() );
237 #endif /* configUSE_TIMERS */
239 /* Restore original signal mask. */
240 ( void ) pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask, NULL );
244 /*-----------------------------------------------------------*/
246 void vPortEndScheduler( void )
248 struct itimerval itimer;
249 struct sigaction sigtick;
250 Thread_t * xCurrentThread;
252 /* Stop the timer and ignore any pending SIGALRMs that would end
253 * up running on the main thread when it is resumed. */
254 itimer.it_value.tv_sec = 0;
255 itimer.it_value.tv_usec = 0;
257 itimer.it_interval.tv_sec = 0;
258 itimer.it_interval.tv_usec = 0;
259 ( void ) setitimer( ITIMER_REAL, &itimer, NULL );
261 sigtick.sa_flags = 0;
262 sigtick.sa_handler = SIG_IGN;
263 sigemptyset( &sigtick.sa_mask );
264 sigaction( SIGALRM, &sigtick, NULL );
266 /* Signal the scheduler to exit its loop. */
267 xSchedulerEnd = pdTRUE;
268 ( void ) pthread_kill( hMainThread, SIG_RESUME );
270 xCurrentThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
271 prvSuspendSelf( xCurrentThread );
273 /*-----------------------------------------------------------*/
275 void vPortEnterCritical( void )
277 if( uxCriticalNesting == 0 )
279 vPortDisableInterrupts();
284 /*-----------------------------------------------------------*/
286 void vPortExitCritical( void )
290 /* If we have reached 0 then re-enable the interrupts. */
291 if( uxCriticalNesting == 0 )
293 vPortEnableInterrupts();
296 /*-----------------------------------------------------------*/
298 static void prvPortYieldFromISR( void )
300 Thread_t * xThreadToSuspend;
301 Thread_t * xThreadToResume;
303 xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
305 vTaskSwitchContext();
307 xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
309 prvSwitchThread( xThreadToResume, xThreadToSuspend );
311 /*-----------------------------------------------------------*/
313 void vPortYield( void )
315 vPortEnterCritical();
317 prvPortYieldFromISR();
321 /*-----------------------------------------------------------*/
323 void vPortDisableInterrupts( void )
325 pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL );
327 /*-----------------------------------------------------------*/
329 void vPortEnableInterrupts( void )
331 pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL );
333 /*-----------------------------------------------------------*/
335 UBaseType_t xPortSetInterruptMask( void )
337 /* Interrupts are always disabled inside ISRs (signals
339 return ( UBaseType_t )0;
341 /*-----------------------------------------------------------*/
343 void vPortClearInterruptMask( UBaseType_t uxMask )
347 /*-----------------------------------------------------------*/
349 static uint64_t prvGetTimeNs( void )
353 clock_gettime( CLOCK_MONOTONIC, &t );
355 return ( uint64_t )t.tv_sec * ( uint64_t )1000000000UL + ( uint64_t )t.tv_nsec;
358 static uint64_t prvStartTimeNs;
360 /* commented as part of the code below in vPortSystemTickHandler,
361 * to adjust timing according to full demo requirements */
362 /* static uint64_t prvTickCount; */
365 * Setup the systick timer to generate the tick interrupts at the required
368 void prvSetupTimerInterrupt( void )
370 struct itimerval itimer;
373 /* Initialise the structure with the current timer information. */
374 iRet = getitimer( ITIMER_REAL, &itimer );
378 prvFatalError( "getitimer", errno );
381 /* Set the interval between timer events. */
382 itimer.it_interval.tv_sec = 0;
383 itimer.it_interval.tv_usec = portTICK_RATE_MICROSECONDS;
385 /* Set the current count-down. */
386 itimer.it_value.tv_sec = 0;
387 itimer.it_value.tv_usec = portTICK_RATE_MICROSECONDS;
389 /* Set-up the timer interrupt. */
390 iRet = setitimer( ITIMER_REAL, &itimer, NULL );
394 prvFatalError( "setitimer", errno );
397 prvStartTimeNs = prvGetTimeNs();
399 /*-----------------------------------------------------------*/
401 static void vPortSystemTickHandler( int sig )
403 Thread_t * pxThreadToSuspend;
404 Thread_t * pxThreadToResume;
408 /* uint64_t xExpectedTicks; */
410 uxCriticalNesting++; /* Signals are blocked in this signal handler. */
412 #if ( configUSE_PREEMPTION == 1 )
413 pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
416 /* Tick Increment, accounting for any lost signals or drift in
420 * Comment code to adjust timing according to full demo requirements
421 * xExpectedTicks = (prvGetTimeNs() - prvStartTimeNs)
422 * / (portTICK_RATE_MICROSECONDS * 1000);
424 xTaskIncrementTick();
427 * } while (prvTickCount < xExpectedTicks);
430 #if ( configUSE_PREEMPTION == 1 )
431 /* Select Next Task. */
432 vTaskSwitchContext();
434 pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
436 prvSwitchThread( pxThreadToResume, pxThreadToSuspend );
441 /*-----------------------------------------------------------*/
443 void vPortThreadDying( void * pxTaskToDelete,
444 volatile BaseType_t * pxPendYield )
446 Thread_t * pxThread = prvGetThreadFromTask( pxTaskToDelete );
448 ( void ) pxPendYield;
450 pxThread->xDying = pdTRUE;
453 void vPortCancelThread( void * pxTaskToDelete )
455 Thread_t * pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete );
458 * The thread has already been suspended so it can be safely cancelled.
460 pthread_cancel( pxThreadToCancel->pthread );
461 pthread_join( pxThreadToCancel->pthread, NULL );
462 event_delete( pxThreadToCancel->ev );
464 /*-----------------------------------------------------------*/
466 static void * prvWaitForStart( void * pvParams )
468 Thread_t * pxThread = pvParams;
470 prvSuspendSelf( pxThread );
472 /* Resumed for the first time, unblocks all signals. */
473 uxCriticalNesting = 0;
474 vPortEnableInterrupts();
476 /* Call the task's entry point. */
477 pxThread->pxCode( pxThread->pvParams );
479 /* A function that implements a task must not exit or attempt to return to
480 * its caller as there is nothing to return to. If a task wants to exit it
481 * should instead call vTaskDelete( NULL ). Artificially force an assert()
482 * to be triggered if configASSERT() is defined, so application writers can
483 * catch the error. */
484 configASSERT( pdFALSE );
488 /*-----------------------------------------------------------*/
490 static void prvSwitchThread( Thread_t * pxThreadToResume,
491 Thread_t * pxThreadToSuspend )
493 BaseType_t uxSavedCriticalNesting;
495 if( pxThreadToSuspend != pxThreadToResume )
500 * The critical section nesting is per-task, so save it on the
501 * stack of the current (suspending thread), restoring it when
502 * we switch back to this task.
504 uxSavedCriticalNesting = uxCriticalNesting;
506 prvResumeThread( pxThreadToResume );
508 if( pxThreadToSuspend->xDying == pdTRUE )
510 pthread_exit( NULL );
513 prvSuspendSelf( pxThreadToSuspend );
515 uxCriticalNesting = uxSavedCriticalNesting;
518 /*-----------------------------------------------------------*/
520 static void prvSuspendSelf( Thread_t * thread )
523 * Suspend this thread by waiting for a pthread_cond_signal event.
525 * A suspended thread must not handle signals (interrupts) so
526 * all signals must be blocked by calling this from:
528 * - Inside a critical section (vPortEnterCritical() /
529 * vPortExitCritical()).
531 * - From a signal handler that has all signals masked.
533 * - A thread with all signals blocked with pthread_sigmask().
535 event_wait( thread->ev );
538 /*-----------------------------------------------------------*/
540 static void prvResumeThread( Thread_t * xThreadId )
542 if( pthread_self() != xThreadId->pthread )
544 event_signal( xThreadId->ev );
547 /*-----------------------------------------------------------*/
549 static void prvSetupSignalsAndSchedulerPolicy( void )
551 struct sigaction sigtick;
554 hMainThread = pthread_self();
556 /* Initialise common signal masks. */
557 sigfillset( &xAllSignals );
559 /* Don't block SIGINT so this can be used to break into GDB while
560 * in a critical section. */
561 sigdelset( &xAllSignals, SIGINT );
564 * Block all signals in this thread so all new threads
565 * inherits this mask.
567 * When a thread is resumed for the first time, all signals
570 ( void ) pthread_sigmask( SIG_SETMASK,
572 &xSchedulerOriginalSignalMask );
574 sigtick.sa_flags = 0;
575 sigtick.sa_handler = vPortSystemTickHandler;
576 sigfillset( &sigtick.sa_mask );
578 iRet = sigaction( SIGALRM, &sigtick, NULL );
582 prvFatalError( "sigaction", errno );
585 /*-----------------------------------------------------------*/
587 uint32_t ulPortGetRunTime( void )
593 return ( uint32_t ) xTimes.tms_utime;
595 /*-----------------------------------------------------------*/