/* | |
* 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_mutex.c | |
* @brief Implementation of mutex 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" | |
#include "FreeRTOS_POSIX/utils.h" | |
/** | |
* @brief Initialize a PTHREAD_MUTEX_INITIALIZER mutex. | |
* | |
* PTHREAD_MUTEX_INITIALIZER sets a flag for a mutex to be initialized later. | |
* This function performs the initialization. | |
* @param[in] pxMutex The mutex to initialize. | |
* | |
* @return nothing | |
*/ | |
static void prvInitializeStaticMutex( pthread_mutex_internal_t * pxMutex ); | |
/** | |
* @brief Default pthread_mutexattr_t. | |
*/ | |
static const pthread_mutexattr_internal_t xDefaultMutexAttributes = | |
{ | |
.iType = PTHREAD_MUTEX_DEFAULT, | |
}; | |
/*-----------------------------------------------------------*/ | |
static void prvInitializeStaticMutex( pthread_mutex_internal_t * pxMutex ) | |
{ | |
/* Check if the mutex needs to be initialized. */ | |
if( pxMutex->xIsInitialized == pdFALSE ) | |
{ | |
/* Mutex initialization must be in a critical section to prevent two threads | |
* from initializing it at the same time. */ | |
taskENTER_CRITICAL(); | |
/* Check again that the mutex is still uninitialized, i.e. it wasn't | |
* initialized while this function was waiting to enter the critical | |
* section. */ | |
if( pxMutex->xIsInitialized == pdFALSE ) | |
{ | |
/* Set the mutex as the default type. */ | |
pxMutex->xAttr.iType = PTHREAD_MUTEX_DEFAULT; | |
/* Call the correct FreeRTOS mutex initialization function based on | |
* the mutex type. */ | |
#if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_RECURSIVE | |
( void ) xSemaphoreCreateRecursiveMutexStatic( &pxMutex->xMutex ); | |
#else | |
( void ) xSemaphoreCreateMutexStatic( &pxMutex->xMutex ); | |
#endif | |
pxMutex->xIsInitialized = pdTRUE; | |
} | |
/* Exit the critical section. */ | |
taskEXIT_CRITICAL(); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
int pthread_mutex_destroy( pthread_mutex_t * mutex ) | |
{ | |
pthread_mutex_internal_t * pxMutex = ( pthread_mutex_internal_t * ) ( mutex ); | |
/* Free resources in use by the mutex. */ | |
if( pxMutex->xTaskOwner == NULL ) | |
{ | |
vSemaphoreDelete( ( SemaphoreHandle_t ) &pxMutex->xMutex ); | |
} | |
return 0; | |
} | |
/*-----------------------------------------------------------*/ | |
int pthread_mutex_init( pthread_mutex_t * mutex, | |
const pthread_mutexattr_t * attr ) | |
{ | |
int iStatus = 0; | |
pthread_mutex_internal_t * pxMutex = ( pthread_mutex_internal_t * ) mutex; | |
if( pxMutex == NULL ) | |
{ | |
/* No memory. */ | |
iStatus = ENOMEM; | |
} | |
if( iStatus == 0 ) | |
{ | |
*pxMutex = FREERTOS_POSIX_MUTEX_INITIALIZER; | |
/* No attributes given, use default attributes. */ | |
if( attr == NULL ) | |
{ | |
pxMutex->xAttr = xDefaultMutexAttributes; | |
} | |
/* Otherwise, use provided attributes. */ | |
else | |
{ | |
pxMutex->xAttr = *( ( pthread_mutexattr_internal_t * ) ( attr ) ); | |
} | |
/* Call the correct FreeRTOS mutex creation function based on mutex type. */ | |
if( pxMutex->xAttr.iType == PTHREAD_MUTEX_RECURSIVE ) | |
{ | |
/* Recursive mutex. */ | |
( void ) xSemaphoreCreateRecursiveMutexStatic( &pxMutex->xMutex ); | |
} | |
else | |
{ | |
/* All other mutex types. */ | |
( void ) xSemaphoreCreateMutexStatic( &pxMutex->xMutex ); | |
} | |
/* Ensure that the FreeRTOS mutex was successfully created. */ | |
if( ( SemaphoreHandle_t ) &pxMutex->xMutex == NULL ) | |
{ | |
/* Failed to create mutex. Set error EAGAIN and free mutex object. */ | |
iStatus = EAGAIN; | |
vPortFree( pxMutex ); | |
} | |
else | |
{ | |
/* Mutex successfully created. */ | |
pxMutex->xIsInitialized = pdTRUE; | |
} | |
} | |
return iStatus; | |
} | |
/*-----------------------------------------------------------*/ | |
int pthread_mutex_lock( pthread_mutex_t * mutex ) | |
{ | |
return pthread_mutex_timedlock( mutex, NULL ); | |
} | |
/*-----------------------------------------------------------*/ | |
int pthread_mutex_timedlock( pthread_mutex_t * mutex, | |
const struct timespec * abstime ) | |
{ | |
int iStatus = 0; | |
pthread_mutex_internal_t * pxMutex = ( pthread_mutex_internal_t * ) ( mutex ); | |
TickType_t xDelay = portMAX_DELAY; | |
BaseType_t xFreeRTOSMutexTakeStatus = pdFALSE; | |
/* If mutex in uninitialized, perform initialization. */ | |
prvInitializeStaticMutex( pxMutex ); | |
/* At this point, the mutex should be initialized. */ | |
configASSERT( pxMutex->xIsInitialized == pdTRUE ); | |
/* Convert abstime to a delay in TickType_t if provided. */ | |
if( abstime != NULL ) | |
{ | |
struct timespec xCurrentTime = { 0 }; | |
/* Get current time */ | |
if( clock_gettime( CLOCK_REALTIME, &xCurrentTime ) != 0 ) | |
{ | |
iStatus = EINVAL; | |
} | |
else | |
{ | |
iStatus = UTILS_AbsoluteTimespecToDeltaTicks( abstime, &xCurrentTime, &xDelay ); | |
} | |
/* If abstime was in the past, still attempt to lock the mutex without | |
* blocking, per POSIX spec. */ | |
if( iStatus == ETIMEDOUT ) | |
{ | |
xDelay = 0; | |
iStatus = 0; | |
} | |
} | |
/* Check if trying to lock a currently owned mutex. */ | |
if( ( iStatus == 0 ) && | |
( pxMutex->xAttr.iType == PTHREAD_MUTEX_ERRORCHECK ) && /* Only PTHREAD_MUTEX_ERRORCHECK type detects deadlock. */ | |
( pxMutex->xTaskOwner == xTaskGetCurrentTaskHandle() ) ) /* Check if locking a currently owned mutex. */ | |
{ | |
iStatus = EDEADLK; | |
} | |
if( iStatus == 0 ) | |
{ | |
/* Call the correct FreeRTOS mutex take function based on mutex type. */ | |
if( pxMutex->xAttr.iType == PTHREAD_MUTEX_RECURSIVE ) | |
{ | |
xFreeRTOSMutexTakeStatus = xSemaphoreTakeRecursive( ( SemaphoreHandle_t ) &pxMutex->xMutex, xDelay ); | |
} | |
else | |
{ | |
xFreeRTOSMutexTakeStatus = xSemaphoreTake( ( SemaphoreHandle_t ) &pxMutex->xMutex, xDelay ); | |
} | |
/* If the mutex was successfully taken, set its owner. */ | |
if( xFreeRTOSMutexTakeStatus == pdPASS ) | |
{ | |
pxMutex->xTaskOwner = xTaskGetCurrentTaskHandle(); | |
} | |
/* Otherwise, the mutex take timed out. */ | |
else | |
{ | |
iStatus = ETIMEDOUT; | |
} | |
} | |
return iStatus; | |
} | |
/*-----------------------------------------------------------*/ | |
int pthread_mutex_trylock( pthread_mutex_t * mutex ) | |
{ | |
int iStatus = 0; | |
struct timespec xTimeout = | |
{ | |
.tv_sec = 0, | |
.tv_nsec = 0 | |
}; | |
/* Attempt to lock with no timeout. */ | |
iStatus = pthread_mutex_timedlock( mutex, &xTimeout ); | |
/* POSIX specifies that this function should return EBUSY instead of | |
* ETIMEDOUT for attempting to lock a locked mutex. */ | |
if( iStatus == ETIMEDOUT ) | |
{ | |
iStatus = EBUSY; | |
} | |
return iStatus; | |
} | |
/*-----------------------------------------------------------*/ | |
int pthread_mutex_unlock( pthread_mutex_t * mutex ) | |
{ | |
int iStatus = 0; | |
pthread_mutex_internal_t * pxMutex = ( pthread_mutex_internal_t * ) ( mutex ); | |
/* If mutex in uninitialized, perform initialization. */ | |
prvInitializeStaticMutex( pxMutex ); | |
/* Check if trying to unlock an unowned mutex. */ | |
if( ( ( pxMutex->xAttr.iType == PTHREAD_MUTEX_ERRORCHECK ) || | |
( pxMutex->xAttr.iType == PTHREAD_MUTEX_RECURSIVE ) ) && | |
( pxMutex->xTaskOwner != xTaskGetCurrentTaskHandle() ) ) | |
{ | |
iStatus = EPERM; | |
} | |
if( iStatus == 0 ) | |
{ | |
/* Suspend the scheduler so that | |
* mutex is unlocked AND owner is updated atomically */ | |
vTaskSuspendAll(); | |
/* Call the correct FreeRTOS mutex unlock function based on mutex type. */ | |
if( pxMutex->xAttr.iType == PTHREAD_MUTEX_RECURSIVE ) | |
{ | |
( void ) xSemaphoreGiveRecursive( ( SemaphoreHandle_t ) &pxMutex->xMutex ); | |
} | |
else | |
{ | |
( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &pxMutex->xMutex ); | |
} | |
/* Update the owner of the mutex. A recursive mutex may still have an | |
* owner, so it should be updated with xSemaphoreGetMutexHolder. */ | |
pxMutex->xTaskOwner = xSemaphoreGetMutexHolder( ( SemaphoreHandle_t ) &pxMutex->xMutex ); | |
/* Resume the scheduler */ | |
( void ) xTaskResumeAll(); | |
} | |
return iStatus; | |
} | |
/*-----------------------------------------------------------*/ | |
int pthread_mutexattr_destroy( pthread_mutexattr_t * attr ) | |
{ | |
( void ) attr; | |
return 0; | |
} | |
/*-----------------------------------------------------------*/ | |
int pthread_mutexattr_gettype( const pthread_mutexattr_t * attr, | |
int * type ) | |
{ | |
pthread_mutexattr_internal_t * pxAttr = ( pthread_mutexattr_internal_t * ) ( attr ); | |
*type = pxAttr->iType; | |
return 0; | |
} | |
/*-----------------------------------------------------------*/ | |
int pthread_mutexattr_init( pthread_mutexattr_t * attr ) | |
{ | |
*( ( pthread_mutexattr_internal_t * ) ( attr ) ) = xDefaultMutexAttributes; | |
return 0; | |
} | |
/*-----------------------------------------------------------*/ | |
int pthread_mutexattr_settype( pthread_mutexattr_t * attr, | |
int type ) | |
{ | |
int iStatus = 0; | |
pthread_mutexattr_internal_t * pxAttr = ( pthread_mutexattr_internal_t * ) ( attr ); | |
switch( type ) | |
{ | |
case PTHREAD_MUTEX_NORMAL: | |
case PTHREAD_MUTEX_RECURSIVE: | |
case PTHREAD_MUTEX_ERRORCHECK: | |
pxAttr->iType = type; | |
break; | |
default: | |
iStatus = EINVAL; | |
break; | |
} | |
return iStatus; | |
} | |
/*-----------------------------------------------------------*/ |