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