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
52 * Note: When using LLDB (the default debugger on macOS) with this port,
53 * suppress SIGUSR1 to prevent debugger interference. This can be
54 * done by adding the following line to ~/.lldbinit:
55 * `process handle SIGUSR1 -n true -p false -s false`
56 *----------------------------------------------------------*/
60 #include "portmacro.h"
69 #include <sys/times.h>
73 /* Scheduler includes. */
77 #include "utils/wait_for_event.h"
78 /*-----------------------------------------------------------*/
80 #define SIG_RESUME SIGUSR1
85 TaskFunction_t pxCode;
92 * The additional per-thread data is stored at the beginning of the
95 static inline Thread_t * prvGetThreadFromTask( TaskHandle_t xTask )
97 StackType_t * pxTopOfStack = *( StackType_t ** ) xTask;
99 return ( Thread_t * ) ( pxTopOfStack + 1 );
102 /*-----------------------------------------------------------*/
104 static pthread_once_t hSigSetupThread = PTHREAD_ONCE_INIT;
105 static pthread_once_t hThreadKeyOnce = PTHREAD_ONCE_INIT;
106 static sigset_t xAllSignals;
107 static sigset_t xSchedulerOriginalSignalMask;
108 static pthread_t hMainThread = ( pthread_t ) NULL;
109 static volatile BaseType_t uxCriticalNesting;
110 static BaseType_t xSchedulerEnd = pdFALSE;
111 static pthread_t hTimerTickThread;
112 static bool xTimerTickThreadShouldRun;
113 static uint64_t prvStartTimeNs;
114 static pthread_key_t xThreadKey = 0;
115 /*-----------------------------------------------------------*/
117 static void prvSetupSignalsAndSchedulerPolicy( void );
118 static void prvSetupTimerInterrupt( void );
119 static void * prvWaitForStart( void * pvParams );
120 static void prvSwitchThread( Thread_t * xThreadToResume,
121 Thread_t * xThreadToSuspend );
122 static void prvSuspendSelf( Thread_t * thread );
123 static void prvResumeThread( Thread_t * xThreadId );
124 static void vPortSystemTickHandler( int sig );
125 static void vPortStartFirstTask( void );
126 static void prvPortYieldFromISR( void );
127 static void prvThreadKeyDestructor( void * pvData );
128 static void prvInitThreadKey( void );
129 static void prvMarkAsFreeRTOSThread( void );
130 static BaseType_t prvIsFreeRTOSThread( void );
131 static void prvDestroyThreadKey( void );
132 /*-----------------------------------------------------------*/
134 static void prvThreadKeyDestructor( void * pvData )
138 /*-----------------------------------------------------------*/
140 static void prvInitThreadKey( void )
142 pthread_key_create( &xThreadKey, prvThreadKeyDestructor );
144 /*-----------------------------------------------------------*/
146 static void prvMarkAsFreeRTOSThread( void )
148 uint8_t * pucThreadData = NULL;
150 ( void ) pthread_once( &hThreadKeyOnce, prvInitThreadKey );
152 pucThreadData = malloc( 1 );
153 configASSERT( pucThreadData != NULL );
157 pthread_setspecific( xThreadKey, pucThreadData );
159 /*-----------------------------------------------------------*/
161 static BaseType_t prvIsFreeRTOSThread( void )
163 uint8_t * pucThreadData = NULL;
164 BaseType_t xRet = pdFALSE;
166 ( void ) pthread_once( &hThreadKeyOnce, prvInitThreadKey );
168 pucThreadData = ( uint8_t * ) pthread_getspecific( xThreadKey );
170 if( ( pucThreadData != NULL ) && ( *pucThreadData == 1 ) )
177 /*-----------------------------------------------------------*/
179 static void prvDestroyThreadKey( void )
181 pthread_key_delete( xThreadKey );
183 /*-----------------------------------------------------------*/
185 static void prvFatalError( const char * pcCall,
186 int iErrno ) __attribute__( ( __noreturn__ ) );
188 void prvFatalError( const char * pcCall,
191 fprintf( stderr, "%s: %s\n", pcCall, strerror( iErrno ) );
194 /*-----------------------------------------------------------*/
196 static void prvPortSetCurrentThreadName( char * pxThreadName )
199 pthread_setname_np( pxThreadName );
201 pthread_setname_np( pthread_self(), pxThreadName );
204 /*-----------------------------------------------------------*/
207 * See header file for description.
209 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
210 StackType_t * pxEndOfStack,
211 TaskFunction_t pxCode,
212 void * pvParameters )
215 pthread_attr_t xThreadAttributes;
219 ( void ) pthread_once( &hSigSetupThread, prvSetupSignalsAndSchedulerPolicy );
222 * Store the additional thread data at the start of the stack.
224 thread = ( Thread_t * ) ( pxTopOfStack + 1 ) - 1;
225 pxTopOfStack = ( StackType_t * ) thread - 1;
227 /* Ensure that there is enough space to store Thread_t on the stack. */
228 ulStackSize = ( size_t ) ( pxTopOfStack + 1 - pxEndOfStack ) * sizeof( *pxTopOfStack );
229 configASSERT( ulStackSize > sizeof( Thread_t ) );
231 thread->pxCode = pxCode;
232 thread->pvParams = pvParameters;
233 thread->xDying = pdFALSE;
235 pthread_attr_init( &xThreadAttributes );
237 thread->ev = event_create();
239 vPortEnterCritical();
241 iRet = pthread_create( &thread->pthread, &xThreadAttributes,
242 prvWaitForStart, thread );
246 prvFatalError( "pthread_create", iRet );
253 /*-----------------------------------------------------------*/
255 void vPortStartFirstTask( void )
257 Thread_t * pxFirstThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
259 /* Start the first task. */
260 prvResumeThread( pxFirstThread );
262 /*-----------------------------------------------------------*/
265 * See header file for description.
267 BaseType_t xPortStartScheduler( void )
272 hMainThread = pthread_self();
273 prvPortSetCurrentThreadName( "Scheduler" );
275 /* Start the timer that generates the tick ISR(SIGALRM).
276 * Interrupts are disabled here already. */
277 prvSetupTimerInterrupt();
280 * Block SIG_RESUME before starting any tasks so the main thread can sigwait on it.
281 * To sigwait on an unblocked signal is undefined.
282 * https://pubs.opengroup.org/onlinepubs/009604499/functions/sigwait.html
284 sigemptyset( &xSignals );
285 sigaddset( &xSignals, SIG_RESUME );
286 ( void ) pthread_sigmask( SIG_BLOCK, &xSignals, NULL );
288 /* Start the first task. */
289 vPortStartFirstTask();
291 /* Wait until signaled by vPortEndScheduler(). */
292 while( xSchedulerEnd != pdTRUE )
294 sigwait( &xSignals, &iSignal );
298 * clear out the variable that is used to end the scheduler, otherwise
299 * subsequent scheduler restarts will end immediately.
301 xSchedulerEnd = pdFALSE;
303 /* Reset pthread_once_t, needed to restart the scheduler again.
304 * memset the internal struct members for MacOS/Linux Compatibility */
306 hSigSetupThread.__sig = _PTHREAD_ONCE_SIG_init;
307 hThreadKeyOnce.__sig = _PTHREAD_ONCE_SIG_init;
308 memset( ( void * ) &hSigSetupThread.__opaque, 0, sizeof( hSigSetupThread.__opaque ) );
309 memset( ( void * ) &hThreadKeyOnce.__opaque, 0, sizeof( hThreadKeyOnce.__opaque ) );
310 #else /* Linux PTHREAD library*/
311 hSigSetupThread = ( pthread_once_t ) PTHREAD_ONCE_INIT;
312 hThreadKeyOnce = ( pthread_once_t ) PTHREAD_ONCE_INIT;
313 #endif /* __APPLE__*/
315 /* Restore original signal mask. */
316 ( void ) pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask, NULL );
318 prvDestroyThreadKey();
322 /*-----------------------------------------------------------*/
324 void vPortEndScheduler( void )
326 Thread_t * pxCurrentThread;
328 /* Stop the timer tick thread. */
329 xTimerTickThreadShouldRun = false;
330 pthread_join( hTimerTickThread, NULL );
332 /* Signal the scheduler to exit its loop. */
333 xSchedulerEnd = pdTRUE;
334 ( void ) pthread_kill( hMainThread, SIG_RESUME );
336 /* Waiting to be deleted here. */
337 if( prvIsFreeRTOSThread() == pdTRUE )
339 pxCurrentThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
340 event_wait( pxCurrentThread->ev );
343 pthread_testcancel();
345 /*-----------------------------------------------------------*/
347 void vPortEnterCritical( void )
349 if( uxCriticalNesting == 0 )
351 vPortDisableInterrupts();
356 /*-----------------------------------------------------------*/
358 void vPortExitCritical( void )
362 /* If we have reached 0 then re-enable the interrupts. */
363 if( uxCriticalNesting == 0 )
365 vPortEnableInterrupts();
368 /*-----------------------------------------------------------*/
370 static void prvPortYieldFromISR( void )
372 Thread_t * xThreadToSuspend;
373 Thread_t * xThreadToResume;
375 xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
377 vTaskSwitchContext();
379 xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
381 prvSwitchThread( xThreadToResume, xThreadToSuspend );
383 /*-----------------------------------------------------------*/
385 void vPortYield( void )
387 /* This must never be called from outside of a FreeRTOS-owned thread, or
388 * the thread could get stuck in a suspended state. */
389 configASSERT( prvIsFreeRTOSThread() == pdTRUE );
391 vPortEnterCritical();
393 prvPortYieldFromISR();
397 /*-----------------------------------------------------------*/
399 void vPortDisableInterrupts( void )
401 if( prvIsFreeRTOSThread() == pdTRUE )
403 pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL );
406 /*-----------------------------------------------------------*/
408 void vPortEnableInterrupts( void )
410 if( prvIsFreeRTOSThread() == pdTRUE )
412 pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL );
415 /*-----------------------------------------------------------*/
417 UBaseType_t xPortSetInterruptMask( void )
419 /* Interrupts are always disabled inside ISRs (signals
421 return ( UBaseType_t ) 0;
423 /*-----------------------------------------------------------*/
425 void vPortClearInterruptMask( UBaseType_t uxMask )
429 /*-----------------------------------------------------------*/
431 static uint64_t prvGetTimeNs( void )
435 clock_gettime( CLOCK_MONOTONIC, &t );
437 return ( uint64_t ) t.tv_sec * ( uint64_t ) 1000000000UL + ( uint64_t ) t.tv_nsec;
439 /*-----------------------------------------------------------*/
441 /* commented as part of the code below in vPortSystemTickHandler,
442 * to adjust timing according to full demo requirements */
443 /* static uint64_t prvTickCount; */
445 static void * prvTimerTickHandler( void * arg )
449 prvMarkAsFreeRTOSThread();
451 prvPortSetCurrentThreadName( "Scheduler timer" );
453 while( xTimerTickThreadShouldRun )
456 * signal to the active task to cause tick handling or
457 * preemption (if enabled)
459 Thread_t * thread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
460 pthread_kill( thread->pthread, SIGALRM );
461 usleep( portTICK_RATE_MICROSECONDS );
466 /*-----------------------------------------------------------*/
469 * Setup the systick timer to generate the tick interrupts at the required
472 void prvSetupTimerInterrupt( void )
474 xTimerTickThreadShouldRun = true;
475 pthread_create( &hTimerTickThread, NULL, prvTimerTickHandler, NULL );
477 prvStartTimeNs = prvGetTimeNs();
479 /*-----------------------------------------------------------*/
481 static void vPortSystemTickHandler( int sig )
483 if( prvIsFreeRTOSThread() == pdTRUE )
485 Thread_t * pxThreadToSuspend;
486 Thread_t * pxThreadToResume;
490 uxCriticalNesting++; /* Signals are blocked in this signal handler. */
492 pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
494 if( xTaskIncrementTick() != pdFALSE )
496 /* Select Next Task. */
497 vTaskSwitchContext();
499 pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
501 prvSwitchThread( pxThreadToResume, pxThreadToSuspend );
508 fprintf( stderr, "vPortSystemTickHandler called from non-FreeRTOS thread\n" );
511 /*-----------------------------------------------------------*/
513 void vPortThreadDying( void * pxTaskToDelete,
514 volatile BaseType_t * pxPendYield )
516 Thread_t * pxThread = prvGetThreadFromTask( pxTaskToDelete );
518 ( void ) pxPendYield;
520 pxThread->xDying = pdTRUE;
522 /*-----------------------------------------------------------*/
524 void vPortCancelThread( void * pxTaskToDelete )
526 Thread_t * pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete );
529 * The thread has already been suspended so it can be safely cancelled.
531 pthread_cancel( pxThreadToCancel->pthread );
532 event_signal( pxThreadToCancel->ev );
533 pthread_join( pxThreadToCancel->pthread, NULL );
534 event_delete( pxThreadToCancel->ev );
536 /*-----------------------------------------------------------*/
538 static void * prvWaitForStart( void * pvParams )
540 Thread_t * pxThread = pvParams;
542 prvMarkAsFreeRTOSThread();
544 prvSuspendSelf( pxThread );
546 /* Resumed for the first time, unblocks all signals. */
547 uxCriticalNesting = 0;
548 vPortEnableInterrupts();
550 /* Set thread name */
551 prvPortSetCurrentThreadName( pcTaskGetName( xTaskGetCurrentTaskHandle() ) );
553 /* Call the task's entry point. */
554 pxThread->pxCode( pxThread->pvParams );
556 /* A function that implements a task must not exit or attempt to return to
557 * its caller as there is nothing to return to. If a task wants to exit it
558 * should instead call vTaskDelete( NULL ). Artificially force an assert()
559 * to be triggered if configASSERT() is defined, so application writers can
560 * catch the error. */
561 configASSERT( pdFALSE );
565 /*-----------------------------------------------------------*/
567 static void prvSwitchThread( Thread_t * pxThreadToResume,
568 Thread_t * pxThreadToSuspend )
570 BaseType_t uxSavedCriticalNesting;
572 if( pxThreadToSuspend != pxThreadToResume )
577 * The critical section nesting is per-task, so save it on the
578 * stack of the current (suspending thread), restoring it when
579 * we switch back to this task.
581 uxSavedCriticalNesting = uxCriticalNesting;
583 prvResumeThread( pxThreadToResume );
585 if( pxThreadToSuspend->xDying == pdTRUE )
587 pthread_exit( NULL );
590 prvSuspendSelf( pxThreadToSuspend );
592 uxCriticalNesting = uxSavedCriticalNesting;
595 /*-----------------------------------------------------------*/
597 static void prvSuspendSelf( Thread_t * thread )
600 * Suspend this thread by waiting for a pthread_cond_signal event.
602 * A suspended thread must not handle signals (interrupts) so
603 * all signals must be blocked by calling this from:
605 * - Inside a critical section (vPortEnterCritical() /
606 * vPortExitCritical()).
608 * - From a signal handler that has all signals masked.
610 * - A thread with all signals blocked with pthread_sigmask().
612 event_wait( thread->ev );
613 pthread_testcancel();
616 /*-----------------------------------------------------------*/
618 static void prvResumeThread( Thread_t * xThreadId )
620 if( pthread_self() != xThreadId->pthread )
622 event_signal( xThreadId->ev );
625 /*-----------------------------------------------------------*/
627 static void prvSetupSignalsAndSchedulerPolicy( void )
629 struct sigaction sigtick;
632 hMainThread = pthread_self();
634 /* Initialise common signal masks. */
635 sigfillset( &xAllSignals );
637 /* Don't block SIGINT so this can be used to break into GDB while
638 * in a critical section. */
639 sigdelset( &xAllSignals, SIGINT );
642 * Block all signals in this thread so all new threads
643 * inherits this mask.
645 * When a thread is resumed for the first time, all signals
648 ( void ) pthread_sigmask( SIG_SETMASK,
650 &xSchedulerOriginalSignalMask );
652 sigtick.sa_flags = 0;
653 sigtick.sa_handler = vPortSystemTickHandler;
654 sigfillset( &sigtick.sa_mask );
656 iRet = sigaction( SIGALRM, &sigtick, NULL );
660 prvFatalError( "sigaction", errno );
663 /*-----------------------------------------------------------*/
665 uint32_t ulPortGetRunTime( void )
671 return ( uint32_t ) xTimes.tms_utime;
673 /*-----------------------------------------------------------*/