text: /*
   * FreeRTOS POSIX V1.1.3
   * Copyright (C) 2020 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 );
  }
  