/* | |
* 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; | |
} | |
/*-----------------------------------------------------------*/ |