mirror of
https://github.com/FreeRTOS/Lab-Project-FreeRTOS-POSIX.git
synced 2025-10-20 13:04:40 +08:00

Update the pthread_cond_t implementation to utilize Task Notifications. In the existing implementation, if multiple threads of different priority are blocked on the same condition, a higher priority thread, in most cases, will unblock multiple times due to taking the underlying semaphore multiple times instead of just once. Switching to Task notifications guarantees that all tasks are equally notified and unblocked. pthread_cond_signal has also been updated to conform to the POSIX specification in that it will unblock the highest priority task waiting on the condition. Resolves #8
350 lines
10 KiB
C
Executable File
350 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 bool prvInitializeStaticCond( pthread_cond_internal_t * pxCond );
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
static bool prvInitializeStaticCond( pthread_cond_internal_t * pxCond )
|
|
{
|
|
unsigned i = 0;
|
|
|
|
/* 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) && (pxCond->tasksLenth > 0) )
|
|
{
|
|
pxCond->xTasksWaiting = pvPortMalloc( sizeof(TaskHandle_t) * pxCond->tasksLenth );
|
|
if( pxCond->xTasksWaiting != NULL )
|
|
{
|
|
/* Initialize Task List to NULL */
|
|
for ( i = 0; i < pxCond->tasksLenth; i++ )
|
|
{
|
|
pxCond->xTasksWaiting[i] = NULL;
|
|
}
|
|
|
|
pxCond->xIsInitialized = pdTRUE;
|
|
}
|
|
}
|
|
|
|
/* Exit the critical section. */
|
|
taskEXIT_CRITICAL();
|
|
}
|
|
|
|
return pxCond->xIsInitialized;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
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. */
|
|
if ( prvInitializeStaticCond( pxCond ) == pdFALSE )
|
|
{
|
|
return ENOMEM;
|
|
}
|
|
|
|
/* Enter critical section to protect task list access and
|
|
* prevent this task from being switched out while notifying
|
|
* all blocked tasks */
|
|
taskENTER_CRITICAL();
|
|
|
|
for( i = 0; i < pxCond->tasksLenth; i++ )
|
|
{
|
|
if ( pxCond->xTasksWaiting[i] != NULL )
|
|
{
|
|
xTaskNotify(pxCond->xTasksWaiting[i], 0, eNoAction);
|
|
pxCond->xTasksWaiting[i] = NULL;
|
|
}
|
|
}
|
|
|
|
/* Exit the critical section. */
|
|
taskEXIT_CRITICAL();
|
|
|
|
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. */
|
|
vPortFree(pxCond->xTasksWaiting);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_cond_init( pthread_cond_t * cond,
|
|
const pthread_condattr_t * attr )
|
|
{
|
|
unsigned i = 0;
|
|
int iStatus = 0;
|
|
pthread_cond_internal_t * pxCond = ( pthread_cond_internal_t * ) cond;
|
|
|
|
/* Silence warnings about unused parameters. */
|
|
( void ) attr;
|
|
|
|
/* Configure default settings */
|
|
pxCond->tasksLenth = posixconfigPTHREAD_COND_MAX_WAITERS;
|
|
|
|
if( pxCond == NULL )
|
|
{
|
|
iStatus = ENOMEM;
|
|
}
|
|
|
|
if( iStatus == 0 )
|
|
{
|
|
pxCond->xTasksWaiting = pvPortMalloc( sizeof(TaskHandle_t) * pxCond->tasksLenth );
|
|
if( pxCond->xTasksWaiting != NULL )
|
|
{
|
|
/* Initialize Task List to NULL */
|
|
for ( i = 0; i < pxCond->tasksLenth; i++ )
|
|
{
|
|
pxCond->xTasksWaiting[i] = NULL;
|
|
}
|
|
pxCond->xIsInitialized = pdTRUE;
|
|
}
|
|
else
|
|
{
|
|
iStatus = ENOMEM;
|
|
}
|
|
}
|
|
|
|
return iStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_cond_signal( pthread_cond_t * cond )
|
|
{
|
|
unsigned i = 0;
|
|
TaskHandle_t* xTaskToNotify = NULL;
|
|
pthread_cond_internal_t * pxCond = ( pthread_cond_internal_t * ) ( cond );
|
|
|
|
/* If the cond is uninitialized, perform initialization. */
|
|
if ( prvInitializeStaticCond( pxCond ) == pdFALSE )
|
|
{
|
|
return ENOMEM;
|
|
}
|
|
|
|
/* Enter critical section to protect task list access and
|
|
* prevent this task from being switched out while notifying
|
|
* the blocked task */
|
|
taskENTER_CRITICAL();
|
|
|
|
for( i = 0; i < pxCond->tasksLenth; i++ )
|
|
{
|
|
if ( pxCond->xTasksWaiting[i] != NULL )
|
|
{
|
|
if ( xTaskToNotify == NULL)
|
|
{
|
|
xTaskToNotify = &pxCond->xTasksWaiting[i];
|
|
continue;
|
|
}
|
|
|
|
/* POSIX Specification states that the scheduling policy shall determine the order
|
|
* in which threads are unblocked. Since signal only unblocks one task, we need to make
|
|
* sure that the thread that is unblocked is of the highest priority that is waiting.
|
|
* Note the specification does not mention the order a thread is unblocked of the same
|
|
* priority.
|
|
*
|
|
* An alternative would be to unblock all threads of the same priority as "spurious"
|
|
* wakeups are allowed, thus allowing the threads to block on the mutex and condition
|
|
* check. */
|
|
if ( uxTaskPriorityGet( *xTaskToNotify ) < uxTaskPriorityGet( pxCond->xTasksWaiting[i] ) )
|
|
{
|
|
xTaskToNotify = &pxCond->xTasksWaiting[i];
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if ( xTaskToNotify != NULL )
|
|
{
|
|
xTaskNotify(*xTaskToNotify, 0, eNoAction);
|
|
*xTaskToNotify = NULL;
|
|
}
|
|
|
|
/* Exit the critical section. */
|
|
taskEXIT_CRITICAL();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_cond_timedwait( pthread_cond_t * cond,
|
|
pthread_mutex_t * mutex,
|
|
const struct timespec * abstime )
|
|
{
|
|
unsigned i = 0;
|
|
int iStatus = 0;
|
|
bool iSet = pdFALSE;
|
|
pthread_cond_internal_t * pxCond = ( pthread_cond_internal_t * ) ( cond );
|
|
TickType_t xDelay = portMAX_DELAY;
|
|
|
|
|
|
/* If the cond is uninitialized, perform initialization. */
|
|
if ( prvInitializeStaticCond( pxCond ) == pdFALSE )
|
|
{
|
|
return ENOMEM;
|
|
}
|
|
|
|
/* 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 );
|
|
}
|
|
}
|
|
|
|
if( iStatus == 0 )
|
|
{
|
|
/* Enter critical section to protect task list access and
|
|
* prevent this task from being switched out while adding
|
|
* itself to the notify list */
|
|
taskENTER_CRITICAL();
|
|
|
|
for( i = 0; i < pxCond->tasksLenth; i++ )
|
|
{
|
|
if ( pxCond->xTasksWaiting[i] == NULL )
|
|
{
|
|
pxCond->xTasksWaiting[i] = xTaskGetCurrentTaskHandle();
|
|
iSet = pdTRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Exit the critical section. */
|
|
taskEXIT_CRITICAL();
|
|
|
|
/* Verify the task was added to the list */
|
|
if ( iSet == pdFALSE )
|
|
{
|
|
/* Note: ENOMEM is not part of the return value list for
|
|
* pthread_cond_timedwait, but the specification only
|
|
* states that EINTR can NOT be returned */
|
|
iStatus = ENOMEM;
|
|
}
|
|
|
|
if ( iStatus == 0 )
|
|
{
|
|
iStatus = pthread_mutex_unlock( mutex );
|
|
}
|
|
|
|
/* Wait on the condition variable. */
|
|
if( iStatus == 0 )
|
|
{
|
|
|
|
if( xTaskNotifyWait( 0, 0, NULL, 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 );
|
|
}
|
|
|
|
/* Enter critical section to protect task list access and
|
|
* remove the task from the waiting list */
|
|
taskENTER_CRITICAL();
|
|
|
|
for( i = 0; i < pxCond->tasksLenth; i++ )
|
|
{
|
|
if ( pxCond->xTasksWaiting[i] == xTaskGetCurrentTaskHandle() )
|
|
{
|
|
pxCond->xTasksWaiting[i] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Exit the critical section. */
|
|
taskEXIT_CRITICAL();
|
|
}
|
|
}
|
|
|
|
return iStatus;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int pthread_cond_wait( pthread_cond_t * cond,
|
|
pthread_mutex_t * mutex )
|
|
{
|
|
return pthread_cond_timedwait( cond, mutex, NULL );
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|