text: /*
   * FreeRTOS Kernel V10.3.0
   * Copyright (C) 2020 Cambridge Consultants Ltd.
   *
   * 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
   *
   * 1 tab == 4 spaces!
   */
  
  /*-----------------------------------------------------------
   * 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.
   *----------------------------------------------------------*/
  
  #include <errno.h>
  #include <pthread.h>
  #include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/time.h>
  #include <sys/times.h>
  #include <time.h>
  
  /* Scheduler includes. */
  #include "FreeRTOS.h"
  #include "task.h"
  #include "utils/wait_for_event.h"
  /*-----------------------------------------------------------*/
  
  #define SIG_RESUME SIGUSR1
  
  typedef struct THREAD
  {
  	pthread_t pthread;
  	pdTASK_CODE 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 sigset_t xResumeSignals;
  static sigset_t xAllSignals;
  static sigset_t xSchedulerOriginalSignalMask;
  static pthread_t hMainThread = ( pthread_t )NULL;
  static volatile portBASE_TYPE uxCriticalNesting;
  /*-----------------------------------------------------------*/
  
  static portBASE_TYPE xSchedulerEnd = pdFALSE;
  /*-----------------------------------------------------------*/
  
  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 prvFatalError( const char *pcCall, int iErrno )
  {
  	fprintf( stderr, "%s: %s\n", pcCall, strerror( iErrno ) );
  	abort();
  }
  
  /*
   * See header file for description.
   */
  portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack,
                                         portSTACK_TYPE *pxEndOfStack,
                                         pdTASK_CODE 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 = (portSTACK_TYPE *)thread - 1;
  	ulStackSize = (pxTopOfStack + 1 - pxEndOfStack) * sizeof(*pxTopOfStack);
  
  	thread->pxCode = pxCode;
  	thread->pvParams = pvParameters;
  	thread->xDying = pdFALSE;
  
  	pthread_attr_init( &xThreadAttributes );
  	pthread_attr_setstack( &xThreadAttributes, pxEndOfStack, ulStackSize );
  
  	thread->ev = event_create();
  
  	vPortEnterCritical();
  
  	iRet = pthread_create( &thread->pthread, &xThreadAttributes,
  						   prvWaitForStart, thread );
  	if ( iRet )
  	{
  		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.
   */
  portBASE_TYPE xPortStartScheduler( void )
  {
  int iSignal;
  sigset_t xSignals;
  
  	hMainThread = pthread_self();
  
  	/* Start the timer that generates the tick ISR(SIGALRM).
  	   Interrupts are disabled here already. */
  	prvSetupTimerInterrupt();
  
  	/* Start the first task. */
  	vPortStartFirstTask();
  
  	/* Wait until signaled by vPortEndScheduler(). */
  	sigemptyset( &xSignals );
  	sigaddset( &xSignals, SIG_RESUME );
  
  	while ( !xSchedulerEnd )
  	{
  		sigwait( &xSignals, &iSignal );
  	}
  
  	/* Restore original signal mask. */
  	(void)pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask,  NULL );
  
  	return 0;
  }
  /*-----------------------------------------------------------*/
  
  void vPortEndScheduler( void )
  {
  struct itimerval itimer;
  struct sigaction sigtick;
  Thread_t *xCurrentThread;
  
  	/* Stop the timer and ignore any pending SIGALRMs that would end
  	 * up running on the main thread when it is resumed. */
  	itimer.it_value.tv_sec = 0;
  	itimer.it_value.tv_usec = 0;
  
  	itimer.it_interval.tv_sec = 0;
  	itimer.it_interval.tv_usec = 0;
  	(void)setitimer( ITIMER_REAL, &itimer, NULL );
  
  	sigtick.sa_flags = 0;
  	sigtick.sa_handler = SIG_IGN;
  	sigemptyset( &sigtick.sa_mask );
  	sigaction( SIGALRM, &sigtick, NULL );
  
  	/* Signal the scheduler to exit its loop. */
  	xSchedulerEnd = pdTRUE;
  	(void)pthread_kill( hMainThread, SIG_RESUME );
  
  	xCurrentThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
  	prvSuspendSelf(xCurrentThread);
  }
  /*-----------------------------------------------------------*/
  
  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();
  	}
  }
  /*-----------------------------------------------------------*/
  
  void vPortYieldFromISR( void )
  {
  Thread_t *xThreadToSuspend;
  Thread_t *xThreadToResume;
  
  	xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
  
  	vTaskSwitchContext();
  
  	xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
  
  	prvSwitchThread( xThreadToResume, xThreadToSuspend );
  }
  /*-----------------------------------------------------------*/
  
  void vPortYield( void )
  {
  	vPortEnterCritical();
  
  	vPortYieldFromISR();
  
  	vPortExitCritical();
  }
  /*-----------------------------------------------------------*/
  
  void vPortDisableInterrupts( void )
  {
  	pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL );
  }
  /*-----------------------------------------------------------*/
  
  void vPortEnableInterrupts( void )
  {
  	pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL );
  }
  /*-----------------------------------------------------------*/
  
  portBASE_TYPE xPortSetInterruptMask( void )
  {
  	/* Interrupts are always disabled inside ISRs (signals
  	   handlers). */
  	return pdTRUE;
  }
  /*-----------------------------------------------------------*/
  
  void vPortClearInterruptMask( portBASE_TYPE xMask )
  {
  }
  /*-----------------------------------------------------------*/
  
  static uint64_t prvGetTimeNs(void)
  {
  struct timespec t;
  
  	clock_gettime(CLOCK_MONOTONIC, &t);
  
  	return t.tv_sec * 1000000000ull + t.tv_nsec;
  }
  
  static uint64_t prvStartTimeNs;
  static uint64_t prvTickCount;
  
  /*
   * Setup the systick timer to generate the tick interrupts at the required
   * frequency.
   */
  void prvSetupTimerInterrupt( void )
  {
  struct itimerval itimer;
  int iRet;
  
  	/* Initialise the structure with the current timer information. */
  	iRet = getitimer( ITIMER_REAL, &itimer );
  	if ( iRet )
  	{
  		prvFatalError( "getitimer", errno );
  	}
  
  	/* Set the interval between timer events. */
  	itimer.it_interval.tv_sec = 0;
  	itimer.it_interval.tv_usec = portTICK_RATE_MICROSECONDS;
  
  	/* Set the current count-down. */
  	itimer.it_value.tv_sec = 0;
  	itimer.it_value.tv_usec = portTICK_RATE_MICROSECONDS;
  
  	/* Set-up the timer interrupt. */
  	iRet = setitimer( ITIMER_REAL, &itimer, NULL );
  	if ( iRet )
  	{
  		prvFatalError( "setitimer", errno );
  	}
  
  	prvStartTimeNs = prvGetTimeNs();
  }
  /*-----------------------------------------------------------*/
  
  static void vPortSystemTickHandler( int sig )
  {
  Thread_t *pxThreadToSuspend;
  Thread_t *pxThreadToResume;
  uint64_t xExpectedTicks;
  
  	uxCriticalNesting++; /* Signals are blocked in this signal handler. */
  
  #if ( configUSE_PREEMPTION == 1 )
  	pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
  #endif
  
  	/* Tick Increment, accounting for any lost signals or drift in
  	 * the timer. */
  	xExpectedTicks = (prvGetTimeNs() - prvStartTimeNs)
  		/ (portTICK_RATE_MICROSECONDS * 1000);
  	do {
  		xTaskIncrementTick();
  		prvTickCount++;
  	} while (prvTickCount < xExpectedTicks);
  
  #if ( configUSE_PREEMPTION == 1 )
  	/* Select Next Task. */
  	vTaskSwitchContext();
  
  	pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
  
  	prvSwitchThread(pxThreadToResume, pxThreadToSuspend);
  #endif
  
  	uxCriticalNesting--;
  }
  /*-----------------------------------------------------------*/
  
  void vPortThreadDying( void *pxTaskToDelete, volatile BaseType_t *pxPendYield )
  {
  Thread_t *pxThread = prvGetThreadFromTask( pxTaskToDelete );
  
  	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 );
  	pthread_join( pxThreadToCancel->pthread, NULL );
  }
  /*-----------------------------------------------------------*/
  
  static void *prvWaitForStart( void * pvParams )
  {
  Thread_t *pxThread = pvParams;
  
  	prvSuspendSelf(pxThread);
  
  	/* Resumed for the first time, unblocks all signals. */
  	uxCriticalNesting = 0;
  	vPortEnableInterrupts();
  
  	/* Call the task's entry point. */
  	pxThread->pxCode( pxThread->pvParams );
  
  	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 )
  		{
  			event_delete(pxThreadToSuspend->ev);
  			pthread_exit( NULL );
  		}
  		prvSuspendSelf( pxThreadToSuspend );
  
  		uxCriticalNesting = uxSavedCriticalNesting;
  	}
  }
  /*-----------------------------------------------------------*/
  
  static void prvSuspendSelf( Thread_t *thread )
  {
  int iSig;
  
  	/*
  	 * 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);
  }
  
  /*-----------------------------------------------------------*/
  
  static void prvResumeThread( Thread_t *xThreadId )
  {
  	if ( pthread_self() != xThreadId->pthread )
  	{
  		event_signal(xThreadId->ev);
  	}
  }
  /*-----------------------------------------------------------*/
  
  static void prvSetupSignalsAndSchedulerPolicy( void )
  {
  struct sigaction sigresume, sigtick;
  int iRet;
  
  	hMainThread = pthread_self();
  
  	/* Initialise common signal masks. */
  	sigemptyset( &xResumeSignals );
  	sigaddset( &xResumeSignals, SIG_RESUME );
  	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 );
  
  	/* SIG_RESUME is only used with sigwait() so doesn't need a
  	   handler. */
  	sigresume.sa_flags = 0;
  	sigresume.sa_handler = SIG_IGN;
  	sigfillset( &sigresume.sa_mask );
  
  	sigtick.sa_flags = 0;
  	sigtick.sa_handler = vPortSystemTickHandler;
  	sigfillset( &sigtick.sa_mask );
  
  	iRet = sigaction( SIG_RESUME, &sigresume, NULL );
  	if ( iRet )
  	{
  		prvFatalError( "sigaction", errno );
  	}
  
  	iRet = sigaction( SIGALRM, &sigtick, NULL );
  	if ( iRet )
  	{
  		prvFatalError( "sigaction", errno );
  	}
  }
  /*-----------------------------------------------------------*/
  
  unsigned long ulPortGetRunTime( void )
  {
  struct tms xTimes;
  
  	times( &xTimes );
  
  	return ( unsigned long ) xTimes.tms_utime;
  }
  /*-----------------------------------------------------------*/
  