blob: 63aec06536ed363a82772a0a3d62c9de51c498af [file] [log] [blame]
/*
* 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;
}
/*-----------------------------------------------------------*/