511 lines
16 KiB
C
Executable File
511 lines
16 KiB
C
Executable File
/*
|
|
* Amazon FreeRTOS POSIX V1.1.0
|
|
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
*
|
|
* 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.
|
|
*
|
|
* http://aws.amazon.com/freertos
|
|
* http://www.FreeRTOS.org
|
|
*/
|
|
|
|
/**
|
|
* @file FreeRTOS_POSIX_pthread.c
|
|
* @brief Implementation of thread functions in pthread.h
|
|
*/
|
|
|
|
/* C standard library includes. */
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
/* FreeRTOS+POSIX includes. */
|
|
#include "FreeRTOS_POSIX.h"
|
|
#include "FreeRTOS_POSIX/errno.h"
|
|
#include "FreeRTOS_POSIX/pthread.h"
|
|
|
|
/**
|
|
* @brief Thread attribute object.
|
|
*/
|
|
typedef struct pthread_attr_internal
|
|
{
|
|
uint16_t usStackSize; /**< Stack size. */
|
|
uint16_t usSchedPriorityDetachState; /**< Schedule priority 15 bits (LSB) Detach state: 1 bits (MSB) */
|
|
} pthread_attr_internal_t;
|
|
|
|
#define pthreadDETACH_STATE_MASK 0x8000
|
|
#define pthreadSCHED_PRIORITY_MASK 0x7FFF
|
|
#define pthreadDETACH_STATE_SHIFT 15
|
|
#define pthreadGET_SCHED_PRIORITY( var ) ( ( var ) & ( pthreadSCHED_PRIORITY_MASK ) )
|
|
#define pthreadIS_JOINABLE( var ) ( ( ( var ) & ( pthreadDETACH_STATE_MASK ) ) == pthreadDETACH_STATE_MASK )
|
|
|
|
/**
|
|
* @brief Thread object.
|
|
*/
|
|
typedef struct pthread_internal
|
|
{
|
|
pthread_attr_internal_t xAttr; /**< Thread attributes. */
|
|
void * ( *pvStartRoutine )( void * ); /**< Application thread function. */
|
|
void * xTaskArg; /**< Arguments for application thread function. */
|
|
TaskHandle_t xTaskHandle; /**< FreeRTOS task handle. */
|
|
StaticSemaphore_t xJoinBarrier; /**< Synchronizes the two callers of pthread_join. */
|
|
StaticSemaphore_t xJoinMutex; /**< Ensures that only one other thread may join this thread. */
|
|
void * xReturn; /**< Return value of pvStartRoutine. */
|
|
} pthread_internal_t;
|
|
|
|
/**
|
|
* @brief Terminates the calling thread.
|
|
*
|
|
* For joinable threads, this function waits for pthread_join. Otherwise,
|
|
* it deletes the thread and frees up resources used by the thread.
|
|
*
|
|
* @return This function does not return.
|
|
*/
|
|
static void prvExitThread( void );
|
|
|
|
/**
|
|
* @brief Wrapper function for the user's thread routine.
|
|
*
|
|
* This function is executed as a FreeRTOS task function.
|
|
* @param[in] pxArg A pointer to a pthread_internal_t.
|
|
*
|
|
* @return nothing
|
|
*/
|
|
static void prvRunThread( void * pxArg );
|
|
|
|
/**
|
|
* @brief Default pthread_attr_t.
|
|
*/
|
|
static const pthread_attr_internal_t xDefaultThreadAttributes =
|
|
{
|
|
.usStackSize = PTHREAD_STACK_MIN,
|
|
.usSchedPriorityDetachState = ( ( uint16_t ) tskIDLE_PRIORITY & pthreadSCHED_PRIORITY_MASK ) | ( PTHREAD_CREATE_JOINABLE << pthreadDETACH_STATE_SHIFT ),
|
|
};
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void prvExitThread( void )
|
|
{
|
|
pthread_internal_t * pxThread = ( pthread_internal_t * ) pthread_self();
|
|
|
|
/* If this thread is joinable, wait for a call to pthread_join. */
|
|
if( pthreadIS_JOINABLE( pxThread->xAttr.usSchedPriorityDetachState ) )
|
|
{
|
|
( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &pxThread->xJoinBarrier );
|
|
|
|
/* Suspend until the call to pthread_join. The caller of pthread_join
|
|
* will perform cleanup. */
|
|
vTaskSuspend( NULL );
|
|
}
|
|
else
|
|
{
|
|
/* For a detached thread, perform cleanup of thread object. */
|
|
vPortFree( pxThread );
|
|
vTaskDelete( NULL );
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static void prvRunThread( void * pxArg )
|
|
{
|
|
pthread_internal_t * pxThread = ( pthread_internal_t * ) pxArg;
|
|
|
|
/* Run the thread routine. */
|
|
pxThread->xReturn = pxThread->pvStartRoutine( ( void * ) pxThread->xTaskArg );
|
|
|
|
/* Exit once finished. This function does not return. */
|
|
prvExitThread();
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_attr_destroy( pthread_attr_t * attr )
|
|
{
|
|
( void ) attr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_attr_getdetachstate( const pthread_attr_t * attr,
|
|
int * detachstate )
|
|
{
|
|
pthread_attr_internal_t * pxAttr = ( pthread_attr_internal_t * ) ( attr );
|
|
|
|
if( pthreadIS_JOINABLE( pxAttr->usSchedPriorityDetachState ) )
|
|
{
|
|
*detachstate = PTHREAD_CREATE_JOINABLE;
|
|
}
|
|
else
|
|
{
|
|
*detachstate = PTHREAD_CREATE_DETACHED;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_attr_getschedparam( const pthread_attr_t * attr,
|
|
struct sched_param * param )
|
|
{
|
|
pthread_attr_internal_t * pxAttr = ( pthread_attr_internal_t * ) ( attr );
|
|
|
|
param->sched_priority = ( int ) ( pthreadGET_SCHED_PRIORITY( pxAttr->usSchedPriorityDetachState ) );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_attr_getstacksize( const pthread_attr_t * attr,
|
|
size_t * stacksize )
|
|
{
|
|
pthread_attr_internal_t * pxAttr = ( pthread_attr_internal_t * ) ( attr );
|
|
|
|
*stacksize = ( size_t ) pxAttr->usStackSize;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_attr_init( pthread_attr_t * attr )
|
|
{
|
|
/* Copy the default values into the new thread attributes object. */
|
|
*( ( pthread_attr_internal_t * ) ( attr ) ) = xDefaultThreadAttributes;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_attr_setdetachstate( pthread_attr_t * attr,
|
|
int detachstate )
|
|
{
|
|
int iStatus = 0;
|
|
pthread_attr_internal_t * pxAttr = ( pthread_attr_internal_t * ) ( attr );
|
|
|
|
if( ( detachstate != PTHREAD_CREATE_DETACHED ) && ( detachstate != PTHREAD_CREATE_JOINABLE ) )
|
|
{
|
|
iStatus = EINVAL;
|
|
}
|
|
else
|
|
{
|
|
/* clear and then set msb bit to detachstate) */
|
|
pxAttr->usSchedPriorityDetachState &= ~pthreadDETACH_STATE_MASK;
|
|
pxAttr->usSchedPriorityDetachState |= ( ( uint16_t ) detachstate << pthreadDETACH_STATE_SHIFT );
|
|
}
|
|
|
|
return iStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_attr_setschedparam( pthread_attr_t * attr,
|
|
const struct sched_param * param )
|
|
{
|
|
int iStatus = 0;
|
|
pthread_attr_internal_t * pxAttr = ( pthread_attr_internal_t * ) ( attr );
|
|
|
|
/* Check for NULL param. */
|
|
if( param == NULL )
|
|
{
|
|
iStatus = EINVAL;
|
|
}
|
|
|
|
/* Ensure that param.sched_priority is valid. */
|
|
if( ( iStatus == 0 ) &&
|
|
( ( param->sched_priority > sched_get_priority_max( SCHED_OTHER ) ) ||
|
|
( param->sched_priority < 0 ) ) )
|
|
{
|
|
iStatus = ENOTSUP;
|
|
}
|
|
|
|
/* Set the sched_param. */
|
|
if( iStatus == 0 )
|
|
{
|
|
/* clear and then set 15 LSB to schedule priority) */
|
|
pxAttr->usSchedPriorityDetachState &= ~pthreadSCHED_PRIORITY_MASK;
|
|
pxAttr->usSchedPriorityDetachState |= ( ( uint16_t ) param->sched_priority );
|
|
}
|
|
|
|
return iStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_attr_setschedpolicy( pthread_attr_t * attr,
|
|
int policy )
|
|
{
|
|
/* Silence warnings about unused parameters. */
|
|
( void ) attr;
|
|
( void ) policy;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_attr_setstacksize( pthread_attr_t * attr,
|
|
size_t stacksize )
|
|
{
|
|
int iStatus = 0;
|
|
pthread_attr_internal_t * pxAttr = ( pthread_attr_internal_t * ) ( attr );
|
|
|
|
if( stacksize < PTHREAD_STACK_MIN )
|
|
{
|
|
iStatus = EINVAL;
|
|
}
|
|
else
|
|
{
|
|
pxAttr->usStackSize = ( uint16_t ) stacksize;
|
|
}
|
|
|
|
return iStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_create( pthread_t * thread,
|
|
const pthread_attr_t * attr,
|
|
void *( *startroutine )( void * ),
|
|
void * arg )
|
|
{
|
|
int iStatus = 0;
|
|
pthread_internal_t * pxThread = NULL;
|
|
struct sched_param xSchedParam = { .sched_priority = tskIDLE_PRIORITY };
|
|
|
|
/* Allocate memory for new thread object. */
|
|
pxThread = ( pthread_internal_t * ) pvPortMalloc( sizeof( pthread_internal_t ) );
|
|
|
|
if( pxThread == NULL )
|
|
{
|
|
/* No memory. */
|
|
iStatus = EAGAIN;
|
|
}
|
|
|
|
if( iStatus == 0 )
|
|
{
|
|
/* No attributes given, use default attributes. */
|
|
if( attr == NULL )
|
|
{
|
|
pxThread->xAttr = xDefaultThreadAttributes;
|
|
}
|
|
/* Otherwise, use provided attributes. */
|
|
else
|
|
{
|
|
pxThread->xAttr = *( ( pthread_attr_internal_t * ) ( attr ) );
|
|
}
|
|
|
|
/* Get priority from attributes */
|
|
xSchedParam.sched_priority = ( int ) pthreadGET_SCHED_PRIORITY( pxThread->xAttr.usSchedPriorityDetachState );
|
|
|
|
/* Set argument and start routine. */
|
|
pxThread->xTaskArg = arg;
|
|
pxThread->pvStartRoutine = startroutine;
|
|
|
|
/* If this thread is joinable, create the synchronization mechanisms for
|
|
* pthread_join. */
|
|
|
|
if( pthreadIS_JOINABLE( pxThread->xAttr.usSchedPriorityDetachState ) )
|
|
{
|
|
/* These calls will not fail when their arguments aren't NULL. */
|
|
( void ) xSemaphoreCreateMutexStatic( &pxThread->xJoinMutex );
|
|
( void ) xSemaphoreCreateBinaryStatic( &pxThread->xJoinBarrier );
|
|
}
|
|
}
|
|
|
|
if( iStatus == 0 )
|
|
{
|
|
/* Suspend all tasks to create a critical section. This ensures that
|
|
* the new thread doesn't exit before a tag is assigned. */
|
|
vTaskSuspendAll();
|
|
|
|
/* Create the FreeRTOS task that will run the pthread. */
|
|
if( xTaskCreate( prvRunThread,
|
|
posixconfigPTHREAD_TASK_NAME,
|
|
( uint16_t ) ( pxThread->xAttr.usStackSize / sizeof( StackType_t ) ),
|
|
( void * ) pxThread,
|
|
xSchedParam.sched_priority,
|
|
&pxThread->xTaskHandle ) != pdPASS )
|
|
{
|
|
/* Task creation failed, no memory. */
|
|
vPortFree( pxThread );
|
|
iStatus = EAGAIN;
|
|
}
|
|
else
|
|
{
|
|
/* Store the pointer to the thread object in the task tag. */
|
|
vTaskSetApplicationTaskTag( pxThread->xTaskHandle, ( TaskHookFunction_t ) pxThread );
|
|
|
|
/* Set the thread object for the user. */
|
|
*thread = ( pthread_t ) pxThread;
|
|
}
|
|
|
|
/* End the critical section. */
|
|
xTaskResumeAll();
|
|
}
|
|
|
|
return iStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_getschedparam( pthread_t thread,
|
|
int * policy,
|
|
struct sched_param * param )
|
|
{
|
|
int iStatus = 0;
|
|
pthread_internal_t * pxThread = ( pthread_internal_t * ) thread;
|
|
|
|
*policy = SCHED_OTHER;
|
|
param->sched_priority = ( int ) pthreadGET_SCHED_PRIORITY( pxThread->xAttr.usSchedPriorityDetachState );
|
|
|
|
return iStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_equal( pthread_t t1,
|
|
pthread_t t2 )
|
|
{
|
|
return t1 == t2;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
void pthread_exit( void * value_ptr )
|
|
{
|
|
pthread_internal_t * pxThread = ( pthread_internal_t * ) pthread_self();
|
|
|
|
/* Set the return value. */
|
|
pxThread->xReturn = value_ptr;
|
|
|
|
/* Exit this thread. */
|
|
prvExitThread();
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_join( pthread_t pthread,
|
|
void ** retval )
|
|
{
|
|
int iStatus = 0;
|
|
pthread_internal_t * pxThread = ( pthread_internal_t * ) pthread;
|
|
|
|
/* Make sure pthread is joinable. Otherwise, this function would block
|
|
* forever waiting for an unjoinable thread. */
|
|
if( !pthreadIS_JOINABLE( pxThread->xAttr.usSchedPriorityDetachState ) )
|
|
{
|
|
iStatus = EDEADLK;
|
|
}
|
|
|
|
/* Only one thread may attempt to join another. Lock the join mutex
|
|
* to prevent other threads from calling pthread_join on the same thread. */
|
|
if( iStatus == 0 )
|
|
{
|
|
if( xSemaphoreTake( ( SemaphoreHandle_t ) &pxThread->xJoinMutex, 0 ) != pdPASS )
|
|
{
|
|
/* Another thread has already joined the requested thread, which would
|
|
* cause this thread to wait forever. */
|
|
iStatus = EDEADLK;
|
|
}
|
|
}
|
|
|
|
/* Attempting to join the calling thread would cause a deadlock. */
|
|
if( iStatus == 0 )
|
|
{
|
|
if( pthread_equal( pthread_self(), pthread ) != 0 )
|
|
{
|
|
iStatus = EDEADLK;
|
|
}
|
|
}
|
|
|
|
if( iStatus == 0 )
|
|
{
|
|
/* Wait for the joining thread to finish. Because this call waits forever,
|
|
* it should never fail. */
|
|
( void ) xSemaphoreTake( ( SemaphoreHandle_t ) &pxThread->xJoinBarrier, portMAX_DELAY );
|
|
|
|
/* Create a critical section to clean up the joined thread. */
|
|
vTaskSuspendAll();
|
|
|
|
/* Release xJoinBarrier and delete it. */
|
|
( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &pxThread->xJoinBarrier );
|
|
vSemaphoreDelete( ( SemaphoreHandle_t ) &pxThread->xJoinBarrier );
|
|
|
|
/* Release xJoinMutex and delete it. */
|
|
( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &pxThread->xJoinMutex );
|
|
vSemaphoreDelete( ( SemaphoreHandle_t ) &pxThread->xJoinMutex );
|
|
|
|
/* Delete the FreeRTOS task that ran the thread. */
|
|
vTaskDelete( pxThread->xTaskHandle );
|
|
|
|
/* Set the return value. */
|
|
if( retval != NULL )
|
|
{
|
|
*retval = pxThread->xReturn;
|
|
}
|
|
|
|
/* Free the thread object. */
|
|
vPortFree( pxThread );
|
|
|
|
/* End the critical section. */
|
|
xTaskResumeAll();
|
|
}
|
|
|
|
return iStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
pthread_t pthread_self( void )
|
|
{
|
|
/* Return a reference to this pthread object, which is stored in the
|
|
* FreeRTOS task tag. */
|
|
return ( pthread_t ) xTaskGetApplicationTaskTag( NULL );
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_setschedparam( pthread_t thread,
|
|
int policy,
|
|
const struct sched_param * param )
|
|
{
|
|
int iStatus = 0;
|
|
|
|
pthread_internal_t * pxThread = ( pthread_internal_t * ) thread;
|
|
|
|
/* Silence compiler warnings about unused parameters. */
|
|
( void ) policy;
|
|
|
|
/* Copy the given sched_param. */
|
|
iStatus = pthread_attr_setschedparam( ( pthread_attr_t * ) &pxThread->xAttr, param );
|
|
|
|
if( iStatus == 0 )
|
|
{
|
|
/* Change the priority of the FreeRTOS task. */
|
|
vTaskPrioritySet( pxThread->xTaskHandle, param->sched_priority );
|
|
}
|
|
|
|
return iStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|