blob: 9c6a734697b012c9cd5e31c20dbf34e8fa7e189d [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_cond.c
* @brief Implementation of condition variable functions in pthread.h
*/
/* C standard library includes. */
#include <limits.h>
/* FreeRTOS+POSIX includes. */
#include "FreeRTOS_POSIX.h"
#include "FreeRTOS_POSIX/errno.h"
#include "FreeRTOS_POSIX/pthread.h"
#include "FreeRTOS_POSIX/utils.h"
#include "atomic.h"
/**
* @brief Initialize a PTHREAD_COND_INITIALIZER cond.
*
* PTHREAD_COND_INITIALIZER sets a flag for a cond to be initialized later.
* This function performs the initialization.
* @param[in] pxCond The cond to initialize.
*
* @return nothing
*/
static void prvInitializeStaticCond( pthread_cond_internal_t * pxCond );
/*-----------------------------------------------------------*/
static void prvInitializeStaticCond( pthread_cond_internal_t * pxCond )
{
/* Check if the condition variable needs to be initialized. */
if( pxCond->xIsInitialized == pdFALSE )
{
/* Cond initialization must be in a critical section to prevent two threads
* from initializing it at the same time. */
taskENTER_CRITICAL();
/* Check again that the cond is still uninitialized, i.e. it wasn't
* initialized while this function was waiting to enter the critical
* section. */
if( pxCond->xIsInitialized == pdFALSE )
{
/* Set the members of the cond. The semaphore create calls will never fail
* when their arguments aren't NULL. */
pxCond->xIsInitialized = pdTRUE;
( void ) xSemaphoreCreateCountingStatic( INT_MAX, 0U, &pxCond->xCondWaitSemaphore );
pxCond->iWaitingThreads = 0;
}
/* Exit the critical section. */
taskEXIT_CRITICAL();
}
}
/**
* @brief Check "atomically" if iLocalWaitingThreads == pxCond->iWaitingThreads and decrement.
*/
static void prvTestAndDecrement( pthread_cond_t * pxCond,
unsigned iLocalWaitingThreads )
{
/* Test local copy of threads waiting is larger than zero. */
while( iLocalWaitingThreads > 0 )
{
/* Test-and-set. Atomically check whether the copy in memory has changed.
* And, if not decrease the copy of threads waiting in memory. */
if( ATOMIC_COMPARE_AND_SWAP_SUCCESS == Atomic_CompareAndSwap_u32( ( uint32_t * ) &pxCond->iWaitingThreads, ( uint32_t ) iLocalWaitingThreads - 1, ( uint32_t ) iLocalWaitingThreads ) )
{
/* Signal one succeeded. Break. */
break;
}
/* Local copy may be out dated. Reload, and retry. */
iLocalWaitingThreads = pxCond->iWaitingThreads;
}
}
/*-----------------------------------------------------------*/
int pthread_cond_broadcast( pthread_cond_t * cond )
{
unsigned i = 0;
pthread_cond_internal_t * pxCond = ( pthread_cond_internal_t * ) ( cond );
/* If the cond is uninitialized, perform initialization. */
prvInitializeStaticCond( pxCond );
/* Local copy of number of threads waiting. */
unsigned iLocalWaitingThreads = pxCond->iWaitingThreads;
/* Test local copy of threads waiting is larger than zero. */
while( iLocalWaitingThreads > 0 )
{
/* Test-and-set. Atomically check whether the copy in memory has changed.
* And, if not set the copy of threads waiting in memory to zero. */
if( ATOMIC_COMPARE_AND_SWAP_SUCCESS == Atomic_CompareAndSwap_u32( ( uint32_t * ) &pxCond->iWaitingThreads, 0, ( uint32_t ) iLocalWaitingThreads ) )
{
/* Unblock all. */
for( i = 0; i < iLocalWaitingThreads; i++ )
{
( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &pxCond->xCondWaitSemaphore );
}
break;
}
/* Local copy is out dated. Reload, and retry. */
iLocalWaitingThreads = pxCond->iWaitingThreads;
}
return 0;
}
/*-----------------------------------------------------------*/
int pthread_cond_destroy( pthread_cond_t * cond )
{
pthread_cond_internal_t * pxCond = ( pthread_cond_internal_t * ) ( cond );
/* Free all resources in use by the cond. */
vSemaphoreDelete( ( SemaphoreHandle_t ) &pxCond->xCondWaitSemaphore );
return 0;
}
/*-----------------------------------------------------------*/
int pthread_cond_init( pthread_cond_t * cond,
const pthread_condattr_t * attr )
{
int iStatus = 0;
pthread_cond_internal_t * pxCond = ( pthread_cond_internal_t * ) cond;
/* Silence warnings about unused parameters. */
( void ) attr;
if( pxCond == NULL )
{
iStatus = ENOMEM;
}
if( iStatus == 0 )
{
/* Set the members of the cond. The semaphore create calls will never fail
* when their arguments aren't NULL. */
pxCond->xIsInitialized = pdTRUE;
( void ) xSemaphoreCreateCountingStatic( INT_MAX, 0U, &pxCond->xCondWaitSemaphore );
pxCond->iWaitingThreads = 0;
}
return iStatus;
}
/*-----------------------------------------------------------*/
int pthread_cond_signal( pthread_cond_t * cond )
{
pthread_cond_internal_t * pxCond = ( pthread_cond_internal_t * ) ( cond );
/* If the cond is uninitialized, perform initialization. */
prvInitializeStaticCond( pxCond );
/* Local copy of number of threads waiting. */
unsigned iLocalWaitingThreads = pxCond->iWaitingThreads;
/* Test local copy of threads waiting is larger than zero. */
while( iLocalWaitingThreads > 0 )
{
/* Test-and-set. Atomically check whether the copy in memory has changed.
* And, if not decrease the copy of threads waiting in memory. */
if( ATOMIC_COMPARE_AND_SWAP_SUCCESS == Atomic_CompareAndSwap_u32( ( uint32_t * ) &pxCond->iWaitingThreads, ( uint32_t ) iLocalWaitingThreads - 1, ( uint32_t ) iLocalWaitingThreads ) )
{
/* Unblock one. */
( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &pxCond->xCondWaitSemaphore );
/* Signal one succeeded. Break. */
break;
}
/* Local copy may be out dated. Reload, and retry. */
iLocalWaitingThreads = pxCond->iWaitingThreads;
}
return 0;
}
/*-----------------------------------------------------------*/
int pthread_cond_timedwait( pthread_cond_t * cond,
pthread_mutex_t * mutex,
const struct timespec * abstime )
{
unsigned iLocalWaitingThreads;
int iStatus = 0;
pthread_cond_internal_t * pxCond = ( pthread_cond_internal_t * ) ( cond );
TickType_t xDelay = portMAX_DELAY;
/* If the cond is uninitialized, perform initialization. */
prvInitializeStaticCond( pxCond );
/* 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 );
}
}
/* Increase the counter of threads blocking on condition variable, then
* unlock mutex. */
if( iStatus == 0 )
{
/* Atomically increments thread waiting by 1, and
* stores number of threads waiting before increment. */
iLocalWaitingThreads = Atomic_Increment_u32( ( uint32_t * ) &pxCond->iWaitingThreads );
iStatus = pthread_mutex_unlock( mutex );
}
/* Wait on the condition variable. */
if( iStatus == 0 )
{
if( xSemaphoreTake( ( SemaphoreHandle_t ) &pxCond->xCondWaitSemaphore,
xDelay ) == pdPASS )
{
/* When successful, relock mutex. */
iStatus = pthread_mutex_lock( mutex );
}
else
{
/* Timeout. Relock mutex and decrement number of waiting threads. */
iStatus = ETIMEDOUT;
( void ) pthread_mutex_lock( mutex );
/* Atomically decrements thread waiting by 1.
* If iLocalWaitingThreads is updated by other thread(s) in between,
* this implementation guarantees to decrement by 1 based on the
* value currently in pxCond->iWaitingThreads. */
prvTestAndDecrement( pxCond, iLocalWaitingThreads + 1 );
}
}
else
{
/* Atomically decrements thread waiting by 1.
* If iLocalWaitingThreads is updated by other thread(s) in between,
* this implementation guarantees to decrement by 1 based on the
* value currently in pxCond->iWaitingThreads. */
prvTestAndDecrement( pxCond, iLocalWaitingThreads + 1 );
}
return iStatus;
}
/*-----------------------------------------------------------*/
int pthread_cond_wait( pthread_cond_t * cond,
pthread_mutex_t * mutex )
{
return pthread_cond_timedwait( cond, mutex, NULL );
}