fd8dae8a25
And make the end of the file consistent. Signed-off-by: Yuhui Zheng <10982575+yuhui-zheng@users.noreply.github.com>
297 lines
10 KiB
C
Executable File
297 lines
10 KiB
C
Executable File
/*
|
|
* 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 );
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|