| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680 |
- /*
- * FreeRTOS Kernel <DEVELOPMENT BRANCH>
- * Copyright (C) 2020 Cambridge Consultants Ltd.
- *
- * SPDX-License-Identifier: MIT
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- * the Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- * https://www.FreeRTOS.org
- * https://github.com/FreeRTOS
- *
- */
- /*-----------------------------------------------------------
- * Implementation of functions defined in portable.h for the Posix port.
- *
- * Each task has a pthread which eases use of standard debuggers
- * (allowing backtraces of tasks etc). Threads for tasks that are not
- * running are blocked in sigwait().
- *
- * Task switch is done by resuming the thread for the next task by
- * signaling the condition variable and then waiting on a condition variable
- * with the current thread.
- *
- * The timer interrupt uses SIGALRM and care is taken to ensure that
- * the signal handler runs only on the thread for the current task.
- *
- * Use of part of the standard C library requires care as some
- * functions can take pthread mutexes internally which can result in
- * deadlocks as the FreeRTOS kernel can switch tasks while they're
- * holding a pthread mutex.
- *
- * stdio (printf() and friends) should be called from a single task
- * only or serialized with a FreeRTOS primitive such as a binary
- * semaphore or mutex.
- *
- * Note: When using LLDB (the default debugger on macOS) with this port,
- * suppress SIGUSR1 to prevent debugger interference. This can be
- * done by adding the following line to ~/.lldbinit:
- * `process handle SIGUSR1 -n true -p false -s false`
- *----------------------------------------------------------*/
- #ifdef __linux__
- #define _GNU_SOURCE
- #endif
- #include "portmacro.h"
- #include <errno.h>
- #include <pthread.h>
- #include <limits.h>
- #include <signal.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/time.h>
- #include <sys/times.h>
- #include <time.h>
- #include <unistd.h>
- /* Scheduler includes. */
- #include "FreeRTOS.h"
- #include "task.h"
- #include "timers.h"
- #include "utils/wait_for_event.h"
- /*-----------------------------------------------------------*/
- #define SIG_RESUME SIGUSR1
- typedef struct THREAD
- {
- pthread_t pthread;
- TaskFunction_t pxCode;
- void * pvParams;
- BaseType_t xDying;
- struct event * ev;
- } Thread_t;
- /*
- * The additional per-thread data is stored at the beginning of the
- * task's stack.
- */
- static inline Thread_t * prvGetThreadFromTask( TaskHandle_t xTask )
- {
- StackType_t * pxTopOfStack = *( StackType_t ** ) xTask;
- return ( Thread_t * ) ( pxTopOfStack + 1 );
- }
- /*-----------------------------------------------------------*/
- static pthread_once_t hSigSetupThread = PTHREAD_ONCE_INIT;
- static pthread_once_t hThreadKeyOnce = PTHREAD_ONCE_INIT;
- static sigset_t xAllSignals;
- static sigset_t xSchedulerOriginalSignalMask;
- static pthread_t hMainThread = ( pthread_t ) NULL;
- static volatile BaseType_t uxCriticalNesting;
- static BaseType_t xSchedulerEnd = pdFALSE;
- static pthread_t hTimerTickThread;
- static bool xTimerTickThreadShouldRun;
- static uint64_t prvStartTimeNs;
- static pthread_key_t xThreadKey = 0;
- /*-----------------------------------------------------------*/
- static void prvSetupSignalsAndSchedulerPolicy( void );
- static void prvSetupTimerInterrupt( void );
- static void * prvWaitForStart( void * pvParams );
- static void prvSwitchThread( Thread_t * xThreadToResume,
- Thread_t * xThreadToSuspend );
- static void prvSuspendSelf( Thread_t * thread );
- static void prvResumeThread( Thread_t * xThreadId );
- static void vPortSystemTickHandler( int sig );
- static void vPortStartFirstTask( void );
- static void prvPortYieldFromISR( void );
- static void prvThreadKeyDestructor( void * pvData );
- static void prvInitThreadKey( void );
- static void prvMarkAsFreeRTOSThread( void );
- static BaseType_t prvIsFreeRTOSThread( void );
- static void prvDestroyThreadKey( void );
- /*-----------------------------------------------------------*/
- static void prvThreadKeyDestructor( void * pvData )
- {
- free( pvData );
- }
- /*-----------------------------------------------------------*/
- static void prvInitThreadKey( void )
- {
- pthread_key_create( &xThreadKey, prvThreadKeyDestructor );
- /* Destroy xThreadKey when the process exits. */
- atexit( prvDestroyThreadKey );
- }
- /*-----------------------------------------------------------*/
- static void prvMarkAsFreeRTOSThread( void )
- {
- uint8_t * pucThreadData = NULL;
- ( void ) pthread_once( &hThreadKeyOnce, prvInitThreadKey );
- pucThreadData = malloc( 1 );
- configASSERT( pucThreadData != NULL );
- *pucThreadData = 1;
- pthread_setspecific( xThreadKey, pucThreadData );
- }
- /*-----------------------------------------------------------*/
- static BaseType_t prvIsFreeRTOSThread( void )
- {
- uint8_t * pucThreadData = NULL;
- BaseType_t xRet = pdFALSE;
- ( void ) pthread_once( &hThreadKeyOnce, prvInitThreadKey );
- pucThreadData = ( uint8_t * ) pthread_getspecific( xThreadKey );
- if( ( pucThreadData != NULL ) && ( *pucThreadData == 1 ) )
- {
- xRet = pdTRUE;
- }
- return xRet;
- }
- /*-----------------------------------------------------------*/
- static void prvDestroyThreadKey( void )
- {
- pthread_key_delete( xThreadKey );
- }
- /*-----------------------------------------------------------*/
- static void prvFatalError( const char * pcCall,
- int iErrno ) __attribute__( ( __noreturn__ ) );
- void prvFatalError( const char * pcCall,
- int iErrno )
- {
- fprintf( stderr, "%s: %s\n", pcCall, strerror( iErrno ) );
- abort();
- }
- /*-----------------------------------------------------------*/
- static void prvPortSetCurrentThreadName( const char * pxThreadName )
- {
- #ifdef __APPLE__
- pthread_setname_np( pxThreadName );
- #else
- pthread_setname_np( pthread_self(), pxThreadName );
- #endif
- }
- /*-----------------------------------------------------------*/
- /*
- * See header file for description.
- */
- StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
- StackType_t * pxEndOfStack,
- TaskFunction_t pxCode,
- void * pvParameters )
- {
- Thread_t * thread;
- pthread_attr_t xThreadAttributes;
- size_t ulStackSize;
- int iRet;
- ( void ) pthread_once( &hSigSetupThread, prvSetupSignalsAndSchedulerPolicy );
- /*
- * Store the additional thread data at the start of the stack.
- */
- thread = ( Thread_t * ) ( pxTopOfStack + 1 ) - 1;
- pxTopOfStack = ( StackType_t * ) thread - 1;
- /* Ensure that there is enough space to store Thread_t on the stack. */
- ulStackSize = ( size_t ) ( pxTopOfStack + 1 - pxEndOfStack ) * sizeof( *pxTopOfStack );
- configASSERT( ulStackSize > sizeof( Thread_t ) );
- ( void ) ulStackSize; /* suppress set but not used warning */
- thread->pxCode = pxCode;
- thread->pvParams = pvParameters;
- thread->xDying = pdFALSE;
- pthread_attr_init( &xThreadAttributes );
- thread->ev = event_create();
- vPortEnterCritical();
- iRet = pthread_create( &thread->pthread, &xThreadAttributes,
- prvWaitForStart, thread );
- if( iRet != 0 )
- {
- prvFatalError( "pthread_create", iRet );
- }
- vPortExitCritical();
- return pxTopOfStack;
- }
- /*-----------------------------------------------------------*/
- void vPortStartFirstTask( void )
- {
- Thread_t * pxFirstThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
- /* Start the first task. */
- prvResumeThread( pxFirstThread );
- }
- /*-----------------------------------------------------------*/
- /*
- * See header file for description.
- */
- BaseType_t xPortStartScheduler( void )
- {
- int iSignal;
- sigset_t xSignals;
- hMainThread = pthread_self();
- prvPortSetCurrentThreadName( "Scheduler" );
- /* Start the timer that generates the tick ISR(SIGALRM).
- * Interrupts are disabled here already. */
- prvSetupTimerInterrupt();
- /*
- * Block SIG_RESUME before starting any tasks so the main thread can sigwait on it.
- * To sigwait on an unblocked signal is undefined.
- * https://pubs.opengroup.org/onlinepubs/009604499/functions/sigwait.html
- */
- sigemptyset( &xSignals );
- sigaddset( &xSignals, SIG_RESUME );
- ( void ) pthread_sigmask( SIG_BLOCK, &xSignals, NULL );
- /* Start the first task. */
- vPortStartFirstTask();
- /* Wait until signaled by vPortEndScheduler(). */
- while( xSchedulerEnd != pdTRUE )
- {
- sigwait( &xSignals, &iSignal );
- }
- /*
- * clear out the variable that is used to end the scheduler, otherwise
- * subsequent scheduler restarts will end immediately.
- */
- xSchedulerEnd = pdFALSE;
- /* Reset pthread_once_t, needed to restart the scheduler again.
- * memset the internal struct members for MacOS/Linux Compatibility */
- #if __APPLE__
- hSigSetupThread.__sig = _PTHREAD_ONCE_SIG_init;
- hThreadKeyOnce.__sig = _PTHREAD_ONCE_SIG_init;
- memset( ( void * ) &hSigSetupThread.__opaque, 0, sizeof( hSigSetupThread.__opaque ) );
- memset( ( void * ) &hThreadKeyOnce.__opaque, 0, sizeof( hThreadKeyOnce.__opaque ) );
- #else /* Linux PTHREAD library*/
- hSigSetupThread = ( pthread_once_t ) PTHREAD_ONCE_INIT;
- hThreadKeyOnce = ( pthread_once_t ) PTHREAD_ONCE_INIT;
- #endif /* __APPLE__*/
- /* Restore original signal mask. */
- ( void ) pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask, NULL );
- return 0;
- }
- /*-----------------------------------------------------------*/
- void vPortEndScheduler( void )
- {
- Thread_t * pxCurrentThread;
- BaseType_t xIsFreeRTOSThread;
- /* Stop the timer tick thread. */
- xTimerTickThreadShouldRun = false;
- pthread_join( hTimerTickThread, NULL );
- /* Check whether the current thread is a FreeRTOS thread.
- * This has to happen before the scheduler is signaled to exit
- * its loop to prevent data races on the thread key. */
- xIsFreeRTOSThread = prvIsFreeRTOSThread();
- /* Signal the scheduler to exit its loop. */
- xSchedulerEnd = pdTRUE;
- ( void ) pthread_kill( hMainThread, SIG_RESUME );
- /* Waiting to be deleted here. */
- if( xIsFreeRTOSThread == pdTRUE )
- {
- pxCurrentThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
- event_wait( pxCurrentThread->ev );
- }
- pthread_testcancel();
- }
- /*-----------------------------------------------------------*/
- void vPortEnterCritical( void )
- {
- if( uxCriticalNesting == 0 )
- {
- vPortDisableInterrupts();
- }
- uxCriticalNesting++;
- }
- /*-----------------------------------------------------------*/
- void vPortExitCritical( void )
- {
- uxCriticalNesting--;
- /* If we have reached 0 then re-enable the interrupts. */
- if( uxCriticalNesting == 0 )
- {
- vPortEnableInterrupts();
- }
- }
- /*-----------------------------------------------------------*/
- static void prvPortYieldFromISR( void )
- {
- Thread_t * xThreadToSuspend;
- Thread_t * xThreadToResume;
- xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
- vTaskSwitchContext();
- xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
- prvSwitchThread( xThreadToResume, xThreadToSuspend );
- }
- /*-----------------------------------------------------------*/
- void vPortYield( void )
- {
- /* This must never be called from outside of a FreeRTOS-owned thread, or
- * the thread could get stuck in a suspended state. */
- configASSERT( prvIsFreeRTOSThread() == pdTRUE );
- vPortEnterCritical();
- prvPortYieldFromISR();
- vPortExitCritical();
- }
- /*-----------------------------------------------------------*/
- void vPortDisableInterrupts( void )
- {
- if( prvIsFreeRTOSThread() == pdTRUE )
- {
- pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL );
- }
- }
- /*-----------------------------------------------------------*/
- void vPortEnableInterrupts( void )
- {
- if( prvIsFreeRTOSThread() == pdTRUE )
- {
- pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL );
- }
- }
- /*-----------------------------------------------------------*/
- UBaseType_t xPortSetInterruptMask( void )
- {
- /* Interrupts are always disabled inside ISRs (signals
- * handlers). */
- return ( UBaseType_t ) 0;
- }
- /*-----------------------------------------------------------*/
- void vPortClearInterruptMask( UBaseType_t uxMask )
- {
- ( void ) uxMask;
- }
- /*-----------------------------------------------------------*/
- static uint64_t prvGetTimeNs( void )
- {
- struct timespec t;
- clock_gettime( CLOCK_MONOTONIC, &t );
- return ( uint64_t ) t.tv_sec * ( uint64_t ) 1000000000UL + ( uint64_t ) t.tv_nsec;
- }
- /*-----------------------------------------------------------*/
- /* commented as part of the code below in vPortSystemTickHandler,
- * to adjust timing according to full demo requirements */
- /* static uint64_t prvTickCount; */
- static void * prvTimerTickHandler( void * arg )
- {
- ( void ) arg;
- prvMarkAsFreeRTOSThread();
- prvPortSetCurrentThreadName( "Scheduler timer" );
- while( xTimerTickThreadShouldRun )
- {
- /*
- * signal to the active task to cause tick handling or
- * preemption (if enabled)
- */
- Thread_t * thread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
- pthread_kill( thread->pthread, SIGALRM );
- usleep( portTICK_RATE_MICROSECONDS );
- }
- return NULL;
- }
- /*-----------------------------------------------------------*/
- /*
- * Setup the systick timer to generate the tick interrupts at the required
- * frequency.
- */
- void prvSetupTimerInterrupt( void )
- {
- xTimerTickThreadShouldRun = true;
- pthread_create( &hTimerTickThread, NULL, prvTimerTickHandler, NULL );
- prvStartTimeNs = prvGetTimeNs();
- }
- /*-----------------------------------------------------------*/
- static void vPortSystemTickHandler( int sig )
- {
- if( prvIsFreeRTOSThread() == pdTRUE )
- {
- Thread_t * pxThreadToSuspend;
- Thread_t * pxThreadToResume;
- ( void ) sig;
- uxCriticalNesting++; /* Signals are blocked in this signal handler. */
- pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
- if( xTaskIncrementTick() != pdFALSE )
- {
- /* Select Next Task. */
- vTaskSwitchContext();
- pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
- prvSwitchThread( pxThreadToResume, pxThreadToSuspend );
- }
- uxCriticalNesting--;
- }
- else
- {
- fprintf( stderr, "vPortSystemTickHandler called from non-FreeRTOS thread\n" );
- }
- }
- /*-----------------------------------------------------------*/
- void vPortThreadDying( void * pxTaskToDelete,
- volatile BaseType_t * pxPendYield )
- {
- Thread_t * pxThread = prvGetThreadFromTask( pxTaskToDelete );
- ( void ) pxPendYield;
- pxThread->xDying = pdTRUE;
- }
- /*-----------------------------------------------------------*/
- void vPortCancelThread( void * pxTaskToDelete )
- {
- Thread_t * pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete );
- /*
- * The thread has already been suspended so it can be safely cancelled.
- */
- pthread_cancel( pxThreadToCancel->pthread );
- event_signal( pxThreadToCancel->ev );
- pthread_join( pxThreadToCancel->pthread, NULL );
- event_delete( pxThreadToCancel->ev );
- }
- /*-----------------------------------------------------------*/
- static void * prvWaitForStart( void * pvParams )
- {
- Thread_t * pxThread = pvParams;
- prvMarkAsFreeRTOSThread();
- prvSuspendSelf( pxThread );
- /* Resumed for the first time, unblocks all signals. */
- uxCriticalNesting = 0;
- vPortEnableInterrupts();
- /* Set thread name */
- prvPortSetCurrentThreadName( pcTaskGetName( xTaskGetCurrentTaskHandle() ) );
- /* Call the task's entry point. */
- pxThread->pxCode( pxThread->pvParams );
- /* A function that implements a task must not exit or attempt to return to
- * its caller as there is nothing to return to. If a task wants to exit it
- * should instead call vTaskDelete( NULL ). Artificially force an assert()
- * to be triggered if configASSERT() is defined, so application writers can
- * catch the error. */
- configASSERT( pdFALSE );
- return NULL;
- }
- /*-----------------------------------------------------------*/
- static void prvSwitchThread( Thread_t * pxThreadToResume,
- Thread_t * pxThreadToSuspend )
- {
- BaseType_t uxSavedCriticalNesting;
- if( pxThreadToSuspend != pxThreadToResume )
- {
- /*
- * Switch tasks.
- *
- * The critical section nesting is per-task, so save it on the
- * stack of the current (suspending thread), restoring it when
- * we switch back to this task.
- */
- uxSavedCriticalNesting = uxCriticalNesting;
- prvResumeThread( pxThreadToResume );
- if( pxThreadToSuspend->xDying == pdTRUE )
- {
- pthread_exit( NULL );
- }
- prvSuspendSelf( pxThreadToSuspend );
- uxCriticalNesting = uxSavedCriticalNesting;
- }
- }
- /*-----------------------------------------------------------*/
- static void prvSuspendSelf( Thread_t * thread )
- {
- /*
- * Suspend this thread by waiting for a pthread_cond_signal event.
- *
- * A suspended thread must not handle signals (interrupts) so
- * all signals must be blocked by calling this from:
- *
- * - Inside a critical section (vPortEnterCritical() /
- * vPortExitCritical()).
- *
- * - From a signal handler that has all signals masked.
- *
- * - A thread with all signals blocked with pthread_sigmask().
- */
- event_wait( thread->ev );
- pthread_testcancel();
- }
- /*-----------------------------------------------------------*/
- static void prvResumeThread( Thread_t * xThreadId )
- {
- if( pthread_self() != xThreadId->pthread )
- {
- event_signal( xThreadId->ev );
- }
- }
- /*-----------------------------------------------------------*/
- static void prvSetupSignalsAndSchedulerPolicy( void )
- {
- struct sigaction sigtick;
- int iRet;
- hMainThread = pthread_self();
- /* Initialise common signal masks. */
- sigfillset( &xAllSignals );
- /* Don't block SIGINT so this can be used to break into GDB while
- * in a critical section. */
- sigdelset( &xAllSignals, SIGINT );
- /*
- * Block all signals in this thread so all new threads
- * inherits this mask.
- *
- * When a thread is resumed for the first time, all signals
- * will be unblocked.
- */
- ( void ) pthread_sigmask( SIG_SETMASK,
- &xAllSignals,
- &xSchedulerOriginalSignalMask );
- sigtick.sa_flags = 0;
- sigtick.sa_handler = vPortSystemTickHandler;
- sigfillset( &sigtick.sa_mask );
- iRet = sigaction( SIGALRM, &sigtick, NULL );
- if( iRet == -1 )
- {
- prvFatalError( "sigaction", errno );
- }
- }
- /*-----------------------------------------------------------*/
- uint32_t ulPortGetRunTime( void )
- {
- struct tms xTimes;
- times( &xTimes );
- return ( uint32_t ) xTimes.tms_utime;
- }
- /*-----------------------------------------------------------*/
|