/* * 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 /* 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 ); } /*-----------------------------------------------------------*/