/* * 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_barrier.c * @brief Implementation of barrier 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 "atomic.h" /* * @brief barrier max count * * Barriers are implemented on FreeRTOS event groups, of which 8 bits are usable * when configUSE_16_BIT_TICKS is 1. Otherwise, 24 bits are usable. */ /**@{ */ #if ( configUSE_16_BIT_TICKS == 1 ) #define posixPTHREAD_BARRIER_MAX_COUNT ( 8 ) #else #define posixPTHREAD_BARRIER_MAX_COUNT ( 24 ) #endif /**@} */ /*-----------------------------------------------------------*/ int pthread_barrier_destroy( pthread_barrier_t * barrier ) { pthread_barrier_internal_t * pxBarrier = ( pthread_barrier_internal_t * ) ( barrier ); /* Free all resources used by the barrier. */ ( void ) vEventGroupDelete( ( EventGroupHandle_t ) &pxBarrier->xBarrierEventGroup ); ( void ) vSemaphoreDelete( ( SemaphoreHandle_t ) &pxBarrier->xThreadCountSemaphore ); return 0; } /*-----------------------------------------------------------*/ int pthread_barrier_init( pthread_barrier_t * barrier, const pthread_barrierattr_t * attr, unsigned count ) { int iStatus = 0; pthread_barrier_internal_t * pxNewBarrier = ( pthread_barrier_internal_t * ) ( barrier ); /* Silence warnings about unused parameters. */ ( void ) attr; /* Ensure count is greater than 0. */ if( count == 0 ) { iStatus = EINVAL; } /* Ensure that count will fit in a FreeRTOS event group. */ if( iStatus == 0 ) { if( count > posixPTHREAD_BARRIER_MAX_COUNT ) { /* No memory exists in the event group for more than * posixPTHREAD_BARRIER_MAX_COUNT threads. */ iStatus = ENOMEM; } } if( iStatus == 0 ) { /* Set the current thread count and threshold. */ pxNewBarrier->uThreadCount = 0; pxNewBarrier->uThreshold = count; /* Create the FreeRTOS event group. This call will not fail when its * argument isn't NULL. */ ( void ) xEventGroupCreateStatic( &pxNewBarrier->xBarrierEventGroup ); /* Create the semaphore that prevents more than count threads from being * unblocked by a single successful pthread_barrier_wait. This semaphore * counts down from count and cannot decrement below 0. */ ( void ) xSemaphoreCreateCountingStatic( ( UBaseType_t ) count, /* Max count. */ ( UBaseType_t ) count, /* Initial count. */ &pxNewBarrier->xThreadCountSemaphore ); } return iStatus; } /*-----------------------------------------------------------*/ int pthread_barrier_wait( pthread_barrier_t * barrier ) { int iStatus = 0; unsigned i = 0; /* Loop iterator. */ pthread_barrier_internal_t * pxBarrier = ( pthread_barrier_internal_t * ) ( barrier ); unsigned uThreadNumber = 0; /* Decrement the number of threads waiting on this barrier. This will prevent more * than pxBarrier->uThreshold threads from being unblocked by a single successful * pthread_barrier_wait call. * * This call will never fail because it blocks forever. */ ( void ) xSemaphoreTake( ( SemaphoreHandle_t ) &pxBarrier->xThreadCountSemaphore, portMAX_DELAY ); uThreadNumber = Atomic_Increment_u32( ( uint32_t * ) &pxBarrier->uThreadCount ); /* Set the bit in the event group representing this thread, then wait for the other * threads to set their bit. This call should wait forever until all threads have set * their bit, so the return value is ignored. */ ( void ) xEventGroupSync( ( EventGroupHandle_t ) &pxBarrier->xBarrierEventGroup, 1 << uThreadNumber, /* Which bit in the event group to set. */ ( 1 << pxBarrier->uThreshold ) - 1, /* Wait for all threads to set their bits. */ portMAX_DELAY ); /* The first thread to enter the barrier gets PTHREAD_BARRIER_SERIAL_THREAD as its * return value and resets xThreadCountSemaphore. */ if( uThreadNumber == 0 ) { iStatus = PTHREAD_BARRIER_SERIAL_THREAD; /* uThreadCount can be safely changed without locking xThreadCountMutex * because xThreadCountSemaphore is currently 0. */ pxBarrier->uThreadCount = 0; /* Reset xThreadCountSemaphore. This allows more threads to enter the * barrier, starting a new cycle. */ for( i = 0; i < pxBarrier->uThreshold; i++ ) { xSemaphoreGive( ( SemaphoreHandle_t ) &pxBarrier->xThreadCountSemaphore ); } } return iStatus; } /*-----------------------------------------------------------*/