mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2025-04-19 21:11:57 -04:00
Improve upon the elegant approach of using signals to cause task/pthreads suspension and scheduler execution by using directed signals. This fixes: - Deadlocks in non-FreeRTOS pthreads - Multiple FreeRTOS tasks(pthreads) incorrectly running at the same time By directing the signals using pthread_kill() the signal handler in the presently running FreeRTOS task/pthread will be called, ensuring that the scheduler runs both in the context of a FreeRTOS task/pthread and from the presently executing FreeRTOS task/pthread. Details ============== The POSIX port uses signals to preempt FreeRTOS tasks (implemented as pthreads), a very neat and elegant approach to forcing tasks/pthreads to suspend and run the scheduler. Signal handlers are process global. Posix timers generate signals when the timer expires, and the signal is sent to the currently running pthread. In systems where there are pthreads that are NOT a result of creating FreeRTOS tasks, such as the entry point thread that calls main(), or user created pthreads, this poses a serious issue. While the POSIX port only allows a single FreeRTOS pthread to run at once, by causing all suspended threads to not be scheduled due to their waiting on a pthread condition variable, this isn't the case with non-FreeRTOS pthreads. Thus it is possible that a non-FreeRTOS pthread is running when the timer expires and the signal is generated. This results in the signal handler running in the non-FreeRTOS thread. The sequence of events results in these events from signal handler context: - vPortSystemTickHandler() being called - The scheduler running - Selecting another FreeRTOS task to run and switching the active task - The newly selected task released from suspension by pthread_cond_signal() - The presently active thread calling event_wait() - The pthread calling pthread_cond_wait(), suspending the thread and allowing the host OS scheduler to schedule another thread to run. If this occurs from a non-FreeRTOS thread this results in: - The active FreeRTOS pthread (Task A/Thread A) continuing to run (as the signal handler that calls event_wait() ran instead in a non-FreeRTOS pthread. - The pthread where the signal handler did run (Thread B) will call event_wait() and pthread_cond_wait(), but on the condition variable of the previously active FreeRTOS task, oops. This causes the non-FreeRTOS pthread to block unexpectedly relative to what the developer might have expected. - The newly selected FreeRTOS Task (Task C/Thread C) will resume and start running. At this point Task A/Thread A is running concurrently with Task C/Thread C. While this may not necessarily be an issue, it does not replicate the expected behavior of a single Task running at once. Note that Thread B will resume if/when Task A/ThreadA is switched to. However, this could be delayed by an arbitrary amount of time, or could never occur. Also note that if there are multiple non-FreeRTOS pthreads that Thread D, E, F...etc could suffer the same fate as Thread B, if the scheduler were to suspend Task C/Thread C and resume Task E/Thread E. Implementation ============== Timer details ------------- A standalone pthread for the signal generation thread was chosen, rather than using a posix timer_settime() handler function because the latter creates a temporary pthread for each handler callback. This makes debugging much more difficult due to gdb detecting the creation and destruction of these temporary threads. Signal delivery -------------- While signal handlers are per-thread, it is possible for pthreads to selectively block signals, rather than using thread directed signals. However, the approach of blocking signals in non-FreeRTOS pthreads adds complexity to each of these non-FreeRTOS pthreads including ensuring that these signals are blocked at thread creation, prior to the thread starting up. Directed signals removes the requirement for non-FreeRTOS pthreads to be aware of and take action to protect against these signals, reducing complexity.
586 lines
16 KiB
C
586 lines
16 KiB
C
/*
|
|
* 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.
|
|
*----------------------------------------------------------*/
|
|
#include "portmacro.h"
|
|
|
|
#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>
|
|
#include <unistd.h>
|
|
|
|
#ifdef __APPLE__
|
|
#include <mach/mach_vm.h>
|
|
#endif
|
|
|
|
/* 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 sigset_t xAllSignals;
|
|
static sigset_t xSchedulerOriginalSignalMask;
|
|
static pthread_t hMainThread = ( pthread_t ) NULL;
|
|
static volatile BaseType_t uxCriticalNesting;
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static pthread_t hTimerTickThread;
|
|
static bool xTimerTickThreadShouldRun;
|
|
|
|
static BaseType_t 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 prvPortYieldFromISR( void );
|
|
/*-----------------------------------------------------------*/
|
|
|
|
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();
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
#ifdef __APPLE__
|
|
pxEndOfStack = ( StackType_t * ) mach_vm_round_page( pxEndOfStack );
|
|
#endif
|
|
|
|
ulStackSize = ( size_t ) ( pxTopOfStack + 1 - pxEndOfStack ) * sizeof( *pxTopOfStack );
|
|
|
|
#ifdef __APPLE__
|
|
ulStackSize = mach_vm_trunc_page( ulStackSize );
|
|
#endif
|
|
|
|
thread->pxCode = pxCode;
|
|
thread->pvParams = pvParameters;
|
|
thread->xDying = pdFALSE;
|
|
|
|
pthread_attr_init( &xThreadAttributes );
|
|
iRet = pthread_attr_setstack( &xThreadAttributes, pxEndOfStack, ulStackSize );
|
|
|
|
if( iRet != 0 )
|
|
{
|
|
fprintf( stderr, "[WARN] pthread_attr_setstack failed with return value: %d. Default stack will be used.\n", iRet );
|
|
fprintf( stderr, "[WARN] Increase the stack size to PTHREAD_STACK_MIN.\n" );
|
|
}
|
|
|
|
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();
|
|
|
|
/* 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 );
|
|
}
|
|
|
|
/* asking timer thread to shut down */
|
|
xTimerTickThreadShouldRun = false;
|
|
pthread_join( hTimerTickThread, NULL );
|
|
|
|
/* Cancel the Idle task and free its resources */
|
|
#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 )
|
|
vPortCancelThread( xTaskGetIdleTaskHandle() );
|
|
#endif
|
|
|
|
#if ( configUSE_TIMERS == 1 )
|
|
/* Cancel the Timer task and free its resources */
|
|
vPortCancelThread( xTimerGetTimerDaemonTaskHandle() );
|
|
#endif /* configUSE_TIMERS */
|
|
|
|
/* Restore original signal mask. */
|
|
( void ) pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask, NULL );
|
|
|
|
return 0;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
void vPortEndScheduler( void )
|
|
{
|
|
Thread_t * xCurrentThread;
|
|
|
|
/* 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();
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void prvPortYieldFromISR( void )
|
|
{
|
|
Thread_t * xThreadToSuspend;
|
|
Thread_t * xThreadToResume;
|
|
|
|
xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
|
|
|
|
vTaskSwitchContext();
|
|
|
|
xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
|
|
|
|
prvSwitchThread( xThreadToResume, xThreadToSuspend );
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
void vPortYield( void )
|
|
{
|
|
vPortEnterCritical();
|
|
|
|
prvPortYieldFromISR();
|
|
|
|
vPortExitCritical();
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
void vPortDisableInterrupts( void )
|
|
{
|
|
pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL );
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
void vPortEnableInterrupts( void )
|
|
{
|
|
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;
|
|
}
|
|
|
|
static uint64_t prvStartTimeNs;
|
|
|
|
/* 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)
|
|
{
|
|
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 )
|
|
{
|
|
Thread_t * pxThreadToSuspend;
|
|
Thread_t * pxThreadToResume;
|
|
|
|
( void ) sig;
|
|
|
|
/* 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. */
|
|
|
|
/*
|
|
* Comment code to adjust timing according to full demo requirements
|
|
* 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 );
|
|
|
|
( 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 );
|
|
pthread_join( pxThreadToCancel->pthread, NULL );
|
|
event_delete( pxThreadToCancel->ev );
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
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 );
|
|
|
|
/* 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 );
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
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;
|
|
}
|
|
/*-----------------------------------------------------------*/
|