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 true -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 vPortEnterCritical();
389 prvPortYieldFromISR();
393 /*-----------------------------------------------------------*/
395 void vPortDisableInterrupts( void )
397 if( prvIsFreeRTOSThread() == pdTRUE )
399 pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL );
402 /*-----------------------------------------------------------*/
404 void vPortEnableInterrupts( void )
406 if( prvIsFreeRTOSThread() == pdTRUE )
408 pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL );
411 /*-----------------------------------------------------------*/
413 UBaseType_t xPortSetInterruptMask( void )
415 /* Interrupts are always disabled inside ISRs (signals
417 return ( UBaseType_t ) 0;
419 /*-----------------------------------------------------------*/
421 void vPortClearInterruptMask( UBaseType_t uxMask )
425 /*-----------------------------------------------------------*/
427 static uint64_t prvGetTimeNs( void )
431 clock_gettime( CLOCK_MONOTONIC, &t );
433 return ( uint64_t ) t.tv_sec * ( uint64_t ) 1000000000UL + ( uint64_t ) t.tv_nsec;
435 /*-----------------------------------------------------------*/
437 /* commented as part of the code below in vPortSystemTickHandler,
438 * to adjust timing according to full demo requirements */
439 /* static uint64_t prvTickCount; */
441 static void * prvTimerTickHandler( void * arg )
445 prvMarkAsFreeRTOSThread();
447 prvPortSetCurrentThreadName( "Scheduler timer" );
449 while( xTimerTickThreadShouldRun )
452 * signal to the active task to cause tick handling or
453 * preemption (if enabled)
455 Thread_t * thread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
456 pthread_kill( thread->pthread, SIGALRM );
457 usleep( portTICK_RATE_MICROSECONDS );
462 /*-----------------------------------------------------------*/
465 * Setup the systick timer to generate the tick interrupts at the required
468 void prvSetupTimerInterrupt( void )
470 xTimerTickThreadShouldRun = true;
471 pthread_create( &hTimerTickThread, NULL, prvTimerTickHandler, NULL );
473 prvStartTimeNs = prvGetTimeNs();
475 /*-----------------------------------------------------------*/
477 static void vPortSystemTickHandler( int sig )
479 if( prvIsFreeRTOSThread() == pdTRUE )
481 Thread_t * pxThreadToSuspend;
482 Thread_t * pxThreadToResume;
486 uxCriticalNesting++; /* Signals are blocked in this signal handler. */
488 pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
490 if( xTaskIncrementTick() != pdFALSE )
492 /* Select Next Task. */
493 vTaskSwitchContext();
495 pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
497 prvSwitchThread( pxThreadToResume, pxThreadToSuspend );
504 fprintf( stderr, "vPortSystemTickHandler called from non-FreeRTOS thread\n" );
507 /*-----------------------------------------------------------*/
509 void vPortThreadDying( void * pxTaskToDelete,
510 volatile BaseType_t * pxPendYield )
512 Thread_t * pxThread = prvGetThreadFromTask( pxTaskToDelete );
514 ( void ) pxPendYield;
516 pxThread->xDying = pdTRUE;
518 /*-----------------------------------------------------------*/
520 void vPortCancelThread( void * pxTaskToDelete )
522 Thread_t * pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete );
525 * The thread has already been suspended so it can be safely cancelled.
527 pthread_cancel( pxThreadToCancel->pthread );
528 event_signal( pxThreadToCancel->ev );
529 pthread_join( pxThreadToCancel->pthread, NULL );
530 event_delete( pxThreadToCancel->ev );
532 /*-----------------------------------------------------------*/
534 static void * prvWaitForStart( void * pvParams )
536 Thread_t * pxThread = pvParams;
538 prvMarkAsFreeRTOSThread();
540 prvSuspendSelf( pxThread );
542 /* Resumed for the first time, unblocks all signals. */
543 uxCriticalNesting = 0;
544 vPortEnableInterrupts();
546 /* Set thread name */
547 prvPortSetCurrentThreadName( pcTaskGetName( xTaskGetCurrentTaskHandle() ) );
549 /* Call the task's entry point. */
550 pxThread->pxCode( pxThread->pvParams );
552 /* A function that implements a task must not exit or attempt to return to
553 * its caller as there is nothing to return to. If a task wants to exit it
554 * should instead call vTaskDelete( NULL ). Artificially force an assert()
555 * to be triggered if configASSERT() is defined, so application writers can
556 * catch the error. */
557 configASSERT( pdFALSE );
561 /*-----------------------------------------------------------*/
563 static void prvSwitchThread( Thread_t * pxThreadToResume,
564 Thread_t * pxThreadToSuspend )
566 BaseType_t uxSavedCriticalNesting;
568 if( pxThreadToSuspend != pxThreadToResume )
573 * The critical section nesting is per-task, so save it on the
574 * stack of the current (suspending thread), restoring it when
575 * we switch back to this task.
577 uxSavedCriticalNesting = uxCriticalNesting;
579 prvResumeThread( pxThreadToResume );
581 if( pxThreadToSuspend->xDying == pdTRUE )
583 pthread_exit( NULL );
586 prvSuspendSelf( pxThreadToSuspend );
588 uxCriticalNesting = uxSavedCriticalNesting;
591 /*-----------------------------------------------------------*/
593 static void prvSuspendSelf( Thread_t * thread )
596 * Suspend this thread by waiting for a pthread_cond_signal event.
598 * A suspended thread must not handle signals (interrupts) so
599 * all signals must be blocked by calling this from:
601 * - Inside a critical section (vPortEnterCritical() /
602 * vPortExitCritical()).
604 * - From a signal handler that has all signals masked.
606 * - A thread with all signals blocked with pthread_sigmask().
608 event_wait( thread->ev );
609 pthread_testcancel();
612 /*-----------------------------------------------------------*/
614 static void prvResumeThread( Thread_t * xThreadId )
616 if( pthread_self() != xThreadId->pthread )
618 event_signal( xThreadId->ev );
621 /*-----------------------------------------------------------*/
623 static void prvSetupSignalsAndSchedulerPolicy( void )
625 struct sigaction sigtick;
628 hMainThread = pthread_self();
630 /* Initialise common signal masks. */
631 sigfillset( &xAllSignals );
633 /* Don't block SIGINT so this can be used to break into GDB while
634 * in a critical section. */
635 sigdelset( &xAllSignals, SIGINT );
638 * Block all signals in this thread so all new threads
639 * inherits this mask.
641 * When a thread is resumed for the first time, all signals
644 ( void ) pthread_sigmask( SIG_SETMASK,
646 &xSchedulerOriginalSignalMask );
648 sigtick.sa_flags = 0;
649 sigtick.sa_handler = vPortSystemTickHandler;
650 sigfillset( &sigtick.sa_mask );
652 iRet = sigaction( SIGALRM, &sigtick, NULL );
656 prvFatalError( "sigaction", errno );
659 /*-----------------------------------------------------------*/
661 uint32_t ulPortGetRunTime( void )
667 return ( uint32_t ) xTimes.tms_utime;
669 /*-----------------------------------------------------------*/