blob: f49ceadac239844cc31b542cd7e47890bf406b25 [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_semaphore.c
* @brief Implementation of functions in semaphore.h
*/
/* C standard library includes. */
#include <stddef.h>
/* FreeRTOS+POSIX includes. */
#include "FreeRTOS_POSIX.h"
#include "FreeRTOS_POSIX/errno.h"
#include "FreeRTOS_POSIX/semaphore.h"
#include "FreeRTOS_POSIX/utils.h"
#include "atomic.h"
/*-----------------------------------------------------------*/
int sem_destroy( sem_t * sem )
{
sem_internal_t * pxSem = ( sem_internal_t * ) ( sem );
/* Free the resources in use by the semaphore. */
vSemaphoreDelete( ( SemaphoreHandle_t ) &pxSem->xSemaphore );
return 0;
}
/*-----------------------------------------------------------*/
int sem_getvalue( sem_t * sem,
int * sval )
{
sem_internal_t * pxSem = ( sem_internal_t * ) ( sem );
/* Get value does not need atomic operation, since -- Open Group
* states "the updated value represents an actual semaphore value that
* occurred at some unspecified time during the call, but it need not be the
* actual value of the semaphore when it is returned to the calling process."
*/
*sval = pxSem->value;
return 0;
}
/*-----------------------------------------------------------*/
int sem_init( sem_t * sem,
int pshared,
unsigned value )
{
int iStatus = 0;
sem_internal_t * pxSem = ( sem_internal_t * ) ( sem );
/* Silence warnings about unused parameters. */
( void ) pshared;
/* Check value parameter. */
if( value > SEM_VALUE_MAX )
{
errno = EINVAL;
iStatus = -1;
}
/* value is guaranteed to not exceed INT32_MAX, which is the default value of SEM_VALUE_MAX (0x7FFFU). */
pxSem->value = ( int ) value;
/* Create the FreeRTOS semaphore.
* This is only used to queue threads when no semaphore is available.
* Initializing with semaphore initial count zero.
* This call will not fail because the memory for the semaphore has already been allocated.
*/
if( iStatus == 0 )
{
( void ) xSemaphoreCreateCountingStatic( SEM_VALUE_MAX, 0, &pxSem->xSemaphore );
}
return iStatus;
}
/*-----------------------------------------------------------*/
int sem_post( sem_t * sem )
{
sem_internal_t * pxSem = ( sem_internal_t * ) ( sem );
int iPreviouValue = Atomic_Increment_u32( ( uint32_t * ) &pxSem->value );
/* If previous semaphore value is equal or larger than zero, there is no
* thread waiting for this semaphore. Otherwise (<0), call FreeRTOS interface
* to wake up a thread. */
if( iPreviouValue < 0 )
{
/* Give the semaphore using the FreeRTOS API. */
( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &pxSem->xSemaphore );
}
return 0;
}
/*-----------------------------------------------------------*/
int sem_timedwait( sem_t * sem,
const struct timespec * abstime )
{
int iStatus = 0;
sem_internal_t * pxSem = ( sem_internal_t * ) ( sem );
TickType_t xDelay = portMAX_DELAY;
if( abstime != NULL )
{
/* If the provided timespec is invalid, still attempt to take the
* semaphore without blocking, per POSIX spec. */
if( UTILS_ValidateTimespec( abstime ) == false )
{
xDelay = 0;
iStatus = EINVAL;
}
else
{
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 take the semaphore without
* blocking, per POSIX spec. */
if( iStatus == ETIMEDOUT )
{
xDelay = 0;
}
}
}
int iPreviousValue = Atomic_Decrement_u32( ( uint32_t * ) &pxSem->value );
/* If previous semaphore value is larger than zero, the thread entering this function call
* can take the semaphore without yielding. Else (<=0), calling into FreeRTOS API to yield.
*/
if( iPreviousValue > 0 )
{
/* Under no circumstance shall the function fail with a timeout if the semaphore can be locked immediately. */
iStatus = 0;
}
else
{
/* Take the semaphore using the FreeRTOS API. */
if( xSemaphoreTake( ( SemaphoreHandle_t ) &pxSem->xSemaphore,
xDelay ) != pdTRUE )
{
if( iStatus == 0 )
{
errno = ETIMEDOUT;
}
else
{
errno = iStatus;
}
iStatus = -1;
}
else
{
iStatus = 0;
}
}
return iStatus;
}
/*-----------------------------------------------------------*/
int sem_trywait( sem_t * sem )
{
int iStatus = 0;
/* Setting an absolute timeout of 0 (i.e. in the past) will cause sem_timedwait
* to not block. */
struct timespec xTimeout = { 0 };
iStatus = sem_timedwait( sem, &xTimeout );
/* POSIX specifies that this function should set errno to EAGAIN and not
* ETIMEDOUT. */
if( ( iStatus == -1 ) && ( errno == ETIMEDOUT ) )
{
errno = EAGAIN;
}
return iStatus;
}
/*-----------------------------------------------------------*/
int sem_wait( sem_t * sem )
{
return sem_timedwait( sem, NULL );
}
/*-----------------------------------------------------------*/