Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve POSIX port functionality #914

Merged
merged 14 commits into from
Jan 11, 2024
Merged
137 changes: 77 additions & 60 deletions portable/ThirdParty/GCC/Posix/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
#include <sys/time.h>
#include <sys/times.h>
#include <time.h>
#include <unistd.h>

#ifdef __APPLE__
#include <mach/mach_vm.h>
Expand All @@ -68,6 +69,7 @@
/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "list.h"
#include "timers.h"
#include "utils/wait_for_event.h"
/*-----------------------------------------------------------*/
Expand All @@ -81,6 +83,7 @@ typedef struct THREAD
void * pvParams;
BaseType_t xDying;
struct event * ev;
ListItem_t xThreadListItem;
} Thread_t;

/*
Expand All @@ -101,9 +104,11 @@ 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 List_t xThreadList;
/*-----------------------------------------------------------*/

static void prvSetupSignalsAndSchedulerPolicy( void );
Expand All @@ -127,6 +132,7 @@ void prvFatalError( const char * pcCall,
fprintf( stderr, "%s: %s\n", pcCall, strerror( iErrno ) );
abort();
}
/*-----------------------------------------------------------*/

/*
* See header file for description.
Expand Down Expand Up @@ -163,19 +169,27 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
thread->pvParams = pvParameters;
thread->xDying = pdFALSE;

/* Ensure ulStackSize is at least PTHREAD_STACK_MIN */
ulStackSize = (ulStackSize < PTHREAD_STACK_MIN) ? PTHREAD_STACK_MIN : ulStackSize;

pthread_attr_init( &xThreadAttributes );
iRet = pthread_attr_setstack( &xThreadAttributes, pxEndOfStack, ulStackSize );
iRet = pthread_attr_setstacksize( &xThreadAttributes, ulStackSize );
paulbartell marked this conversation as resolved.
Show resolved Hide resolved

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" );
fprintf( stderr, "[WARN] pthread_attr_setstacksize failed with return value: %d. Default stack size will be used.\n", iRet );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this log message unnecessary now that the stack size is set to be PTHREAD_STACK_MIN if ulStackSize is less than PTHREAD_STACK_SIZE?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pthreaad_attr_setstacksize() could still fail and in those cases I thought it was useful to report it to the user. It should be very rare but there are a number of conditions that could result in it failing and given that this is an OS it seemed like we should handle all error conditions in some manner, even if that's a warning printout since the consequences are usually minimal if this does fail.

}

thread->ev = event_create();

vListInitialiseItem( &thread->xThreadListItem );
listSET_LIST_ITEM_OWNER( &thread->xThreadListItem, thread );

vPortEnterCritical();

/* Add the new thread in xThreadList. */
vListInsertEnd( &xThreadList, &thread->xThreadListItem );

iRet = pthread_create( &thread->pthread, &xThreadAttributes,
prvWaitForStart, thread );

Expand Down Expand Up @@ -206,6 +220,8 @@ BaseType_t xPortStartScheduler( void )
{
int iSignal;
sigset_t xSignals;
ListItem_t * pxIterator;
const ListItem_t * pxEndMarker;

hMainThread = pthread_self();

Expand All @@ -231,15 +247,28 @@ BaseType_t xPortStartScheduler( void )
sigwait( &xSignals, &iSignal );
}

/* Cancel the Idle task and free its resources */
#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 )
vPortCancelThread( xTaskGetIdleTaskHandle() );
#endif
/* Cancel all the running thread. */
pxEndMarker = listGET_END_MARKER( &xThreadList );

for( pxIterator = listGET_HEAD_ENTRY( &xThreadList ); pxIterator != pxEndMarker; pxIterator = listGET_NEXT( pxIterator ) )
{
Thread_t * pxThread = ( Thread_t * ) listGET_LIST_ITEM_OWNER( pxIterator );

pthread_cancel( pxThread->pthread );
event_signal( pxThread->ev );
pthread_join( pxThread->pthread, NULL );
event_delete( pxThread->ev );
}

/*
* clear out the variable that is used to end the scheduler, otherwise
* subsequent scheduler restarts will end immediately.
*/
xSchedulerEnd = pdFALSE;

#if ( configUSE_TIMERS == 1 )
/* Cancel the Timer task and free its resources */
vPortCancelThread( xTimerGetTimerDaemonTaskHandle() );
#endif /* configUSE_TIMERS */
/* Reset the pthread_once_t structure. This is required if the port
* starts the scheduler again. */
hSigSetupThread = PTHREAD_ONCE_INIT;

/* Restore original signal mask. */
( void ) pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask, NULL );
Expand All @@ -250,30 +279,15 @@ BaseType_t xPortStartScheduler( void )

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 );
/* Stop the timer tick thread. */
xTimerTickThreadShouldRun = false;
pthread_join( hTimerTickThread, NULL );

/* Signal the scheduler to exit its loop. */
xSchedulerEnd = pdTRUE;
( void ) pthread_kill( hMainThread, SIG_RESUME );

xCurrentThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() );
prvSuspendSelf( xCurrentThread );
pthread_exit( NULL );
}
/*-----------------------------------------------------------*/

Expand Down Expand Up @@ -359,45 +373,37 @@ static uint64_t prvGetTimeNs( void )

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 )
{
struct itimerval itimer;
int iRet;

/* Initialise the structure with the current timer information. */
iRet = getitimer( ITIMER_REAL, &itimer );

if( iRet == -1 )
{
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 == -1 )
{
prvFatalError( "setitimer", errno );
}
xTimerTickThreadShouldRun = true;
pthread_create( &hTimerTickThread, NULL, prvTimerTickHandler, NULL );

prvStartTimeNs = prvGetTimeNs();
}
Expand Down Expand Up @@ -454,15 +460,22 @@ void vPortThreadDying( void * pxTaskToDelete,

pxThread->xDying = pdTRUE;
}
/*-----------------------------------------------------------*/

void vPortCancelThread( void * pxTaskToDelete )
{
Thread_t * pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete );

/* Remove the thread from xThreadList. */
vPortEnterCritical();
uxListRemove( &pxThreadToCancel->xThreadListItem );
vPortExitCritical();

/*
* 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 );
}
Expand Down Expand Up @@ -538,6 +551,7 @@ static void prvSuspendSelf( Thread_t * thread )
* - A thread with all signals blocked with pthread_sigmask().
*/
event_wait( thread->ev );
pthread_testcancel();
}

/*-----------------------------------------------------------*/
Expand All @@ -558,6 +572,9 @@ static void prvSetupSignalsAndSchedulerPolicy( void )

hMainThread = pthread_self();

/* Setup thread list to record all the task which are not deleted. */
vListInitialise( &xThreadList );

/* Initialise common signal masks. */
sigfillset( &xAllSignals );

Expand Down
Loading