mirror of
https://github.com/FreeRTOS/Lab-Project-FreeRTOS-POSIX.git
synced 2025-10-20 21:51:03 +08:00
After this commit, this repository contains only POSIX source code.
This commit is contained in:
510
FreeRTOS-Plus-POSIX/source/FreeRTOS_POSIX_pthread.c
Executable file
510
FreeRTOS-Plus-POSIX/source/FreeRTOS_POSIX_pthread.c
Executable file
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
Reference in New Issue
Block a user