/* | |
* 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 <stddef.h> | |
/* 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; | |
} |