Merge branch 'feature/ringbuf_uses_event_lists' into 'master'

esp_ringbuf: Replace internal semaphores with task event lists

Closes IDF-875

See merge request espressif/esp-idf!22442
pull/10922/head
Darian 2023-03-02 01:58:44 +08:00
commit 640ba3c029
5 zmienionych plików z 358 dodań i 386 usunięć

Wyświetl plik

@ -1,22 +1,18 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef FREERTOS_RINGBUF_H
#define FREERTOS_RINGBUF_H
#pragma once
#ifndef INC_FREERTOS_H
#error "include FreeRTOS.h" must appear in source files before "include ringbuf.h"
#endif
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <freertos/queue.h>
/**
* Type by which ring buffers are referenced. For example, a call to xRingbufferCreate()
* returns a RingbufHandle_t variable that can then be used as a parameter to
@ -57,18 +53,17 @@ typedef enum {
* buffer's control data structure.
*
*/
#if ( configSUPPORT_STATIC_ALLOCATION == 1)
typedef struct xSTATIC_RINGBUFFER {
/** @cond */ //Doxygen command to hide this structure from API Reference
size_t xDummy1[2];
UBaseType_t uxDummy2;
BaseType_t xDummy3;
void *pvDummy4[11];
StaticSemaphore_t xDummy5[2];
StaticList_t xDummy5[2];
void * pvDummy6;
portMUX_TYPE muxDummy;
/** @endcond */
} StaticRingbuffer_t;
#endif
/**
* @brief Create a ring buffer
@ -111,12 +106,10 @@ RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum);
*
* @return A handle to the created ring buffer
*/
#if ( configSUPPORT_STATIC_ALLOCATION == 1)
RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize,
RingbufferType_t xBufferType,
uint8_t *pucRingbufferStorage,
StaticRingbuffer_t *pxStaticRingbuffer);
#endif
/**
* @brief Insert an item into the ring buffer
@ -133,6 +126,10 @@ RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize,
* the item will occupy will be rounded up to the nearest 32-bit aligned
* size. This is done to ensure all items are always stored in 32-bit
* aligned fashion.
* @note For no-split/allow-split buffers, an xItemSize of 0 will result in
* an item with no data being set (i.e., item only contains the header).
* For byte buffers, an xItemSize of 0 will simply return pdTRUE without
* copying any data.
*
* @return
* - pdTRUE if succeeded
@ -158,6 +155,10 @@ BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer,
* the item will occupy will be rounded up to the nearest 32-bit aligned
* size. This is done to ensure all items are always stored in 32-bit
* aligned fashion.
* @note For no-split/allow-split buffers, an xItemSize of 0 will result in
* an item with no data being set (i.e., item only contains the header).
* For byte buffers, an xItemSize of 0 will simply return pdTRUE without
* copying any data.
*
* @return
* - pdTRUE if succeeded
@ -189,6 +190,8 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer,
* memory that the item will occupy will be rounded up to the nearest 32-bit
* aligned size. This is done to ensure all items are always stored in 32-bit
* aligned fashion.
* @note An xItemSize of 0 will result in a buffer being acquired, but the buffer
* will have a size of 0.
*
* @return
* - pdTRUE if succeeded
@ -223,6 +226,7 @@ BaseType_t xRingbufferSendComplete(RingbufHandle_t xRingbuffer, void *pvItem);
* @param[in] xTicksToWait Ticks to wait for items in the ring buffer.
*
* @note A call to vRingbufferReturnItem() is required after this to free the item retrieved.
* @note It is possible to receive items with a pxItemSize of 0 on no-split/allow split buffers.
*
* @return
* - Pointer to the retrieved item on success; *pxItemSize filled with the length of the item.
@ -243,6 +247,7 @@ void *xRingbufferReceive(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickTy
* @note A call to vRingbufferReturnItemFromISR() is required after this to free the item retrieved.
* @note Byte buffers do not allow multiple retrievals before returning an item
* @note Two calls to RingbufferReceiveFromISR() are required if the bytes wrap around the end of the ring buffer.
* @note It is possible to receive items with a pxItemSize of 0 on no-split/allow split buffers.
*
* @return
* - Pointer to the retrieved item on success; *pxItemSize filled with the length of the item.
@ -267,6 +272,7 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize)
*
* @note Call(s) to vRingbufferReturnItem() is required after this to free up the item(s) retrieved.
* @note This function should only be called on allow-split buffers
* @note It is possible to receive items with a pxItemSize of 0 on allow split buffers.
*
* @return
* - pdTRUE if an item (split or unsplit) was retrieved
@ -295,6 +301,7 @@ BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer,
*
* @note Calls to vRingbufferReturnItemFromISR() is required after this to free up the item(s) retrieved.
* @note This function should only be called on allow-split buffers
* @note It is possible to receive items with a pxItemSize of 0 on allow split buffers.
*
* @return
* - pdTRUE if an item (split or unsplit) was retrieved
@ -343,7 +350,7 @@ void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer,
*
* @param[in] xRingbuffer Ring buffer to retrieve the item from
* @param[out] pxItemSize Pointer to a variable to which the size of the retrieved item will be written.
* @param[in] xMaxSize Maximum number of bytes to return.
* @param[in] xMaxSize Maximum number of bytes to return. Size of 0 simply returns NULL.
*
* @note A call to vRingbufferReturnItemFromISR() is required after this to free up the data received.
* @note This function should only be called on byte buffers
@ -429,14 +436,14 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t xRingbuffer);
size_t xRingbufferGetCurFreeSize(RingbufHandle_t xRingbuffer);
/**
* @brief Add the ring buffer's read semaphore to a queue set.
* @brief Add the ring buffer to a queue set. Notified when data has been written to the ring buffer
*
* The ring buffer's read semaphore indicates that data has been written
* to the ring buffer. This function adds the ring buffer's read semaphore to
* a queue set.
* This function adds the ring buffer to a queue set, thus allowing a task to
* block on multiple queues/ring buffers. The queue set is notified when the new
* data becomes available to read on the ring buffer.
*
* @param[in] xRingbuffer Ring buffer to add to the queue set
* @param[in] xQueueSet Queue set to add the ring buffer's read semaphore to
* @param[in] xQueueSet Queue set to add the ring buffer to
*
* @return
* - pdTRUE on success, pdFALSE otherwise
@ -445,29 +452,32 @@ BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHan
/**
* @brief Check if the selected queue set member is the ring buffer's read semaphore
* @brief Check if the selected queue set member is a particular ring buffer
*
* This API checks if queue set member returned from xQueueSelectFromSet()
* is the read semaphore of this ring buffer. If so, this indicates the ring buffer
* has items waiting to be retrieved.
* This API checks if queue set member returned from xQueueSelectFromSet() is
* a particular ring buffer. If so, this indicates the ring buffer has items
* waiting to be retrieved.
*
* @param[in] xRingbuffer Ring buffer which should be checked
* @param[in] xRingbuffer Ring buffer to check
* @param[in] xMember Member returned from xQueueSelectFromSet
*
* @return
* - pdTRUE when semaphore belongs to ring buffer
* - pdTRUE when selected queue set member is the ring buffer
* - pdFALSE otherwise.
*/
BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_t xMember);
static inline BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_t xMember)
{
return (xMember == (QueueSetMemberHandle_t)xRingbuffer) ? pdTRUE : pdFALSE;
}
/**
* @brief Remove the ring buffer's read semaphore from a queue set.
* @brief Remove the ring buffer from a queue set
*
* This specifically removes a ring buffer's read semaphore from a queue set. The
* read semaphore is used to indicate when data has been written to the ring buffer
* This function removes a ring buffer from a queue set. The ring buffer must have been previously added to the queue
* set using xRingbufferAddToQueueSetRead().
*
* @param[in] xRingbuffer Ring buffer to remove from the queue set
* @param[in] xQueueSet Queue set to remove the ring buffer's read semaphore from
* @param[in] xQueueSet Queue set to remove the ring buffer from
*
* @return
* - pdTRUE on success
@ -506,5 +516,3 @@ void xRingbufferPrintInfo(RingbufHandle_t xRingbuffer);
#ifdef __cplusplus
}
#endif
#endif /* FREERTOS_RINGBUF_H */

Wyświetl plik

@ -12,7 +12,6 @@ entries:
ringbuf: vRingbufferGetInfo (default)
ringbuf: vRingbufferReturnItem (default)
ringbuf: xRingbufferAddToQueueSetRead (default)
ringbuf: xRingbufferCanRead (default)
ringbuf: xRingbufferCreate (default)
ringbuf: xRingbufferCreateStatic (default)
ringbuf: xRingbufferReceive (default)

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -7,10 +7,13 @@
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/list.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/ringbuf.h"
// ------------------------------------------------- Macros and Types --------------------------------------------------
//32-bit alignment macros
#define rbALIGN_MASK (0x03)
#define rbALIGN_SIZE( xSize ) ( ( xSize + rbALIGN_MASK ) & ~rbALIGN_MASK )
@ -21,6 +24,7 @@
#define rbBYTE_BUFFER_FLAG ( ( UBaseType_t ) 2 ) //The ring buffer is a byte buffer
#define rbBUFFER_FULL_FLAG ( ( UBaseType_t ) 4 ) //The ring buffer is currently full (write pointer == free pointer)
#define rbBUFFER_STATIC_FLAG ( ( UBaseType_t ) 8 ) //The ring buffer is statically allocated
#define rbUSING_QUEUE_SET ( ( UBaseType_t ) 16 ) //The ring buffer has been added to a queue set
//Item flags
#define rbITEM_FREE_FLAG ( ( UBaseType_t ) 1 ) //Item has been retrieved and returned by application, free to overwrite
@ -28,15 +32,6 @@
#define rbITEM_SPLIT_FLAG ( ( UBaseType_t ) 4 ) //Valid for RINGBUF_TYPE_ALLOWSPLIT, indicating that rest of the data is wrapped around
#define rbITEM_WRITTEN_FLAG ( ( UBaseType_t ) 8 ) //Item has been written to by the application, thus can be read
//Static allocation related
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
#define rbGET_TX_SEM_HANDLE( pxRingbuffer ) ( (SemaphoreHandle_t) &(pxRingbuffer->xTransSemStatic) )
#define rbGET_RX_SEM_HANDLE( pxRingbuffer ) ( (SemaphoreHandle_t) &(pxRingbuffer->xRecvSemStatic) )
#else
#define rbGET_TX_SEM_HANDLE( pxRingbuffer ) ( pxRingbuffer->xTransSemHandle )
#define rbGET_RX_SEM_HANDLE( pxRingbuffer ) ( pxRingbuffer->xRecvSemHandle )
#endif
typedef struct {
//This size of this structure must be 32-bit aligned
size_t xItemLen;
@ -71,52 +66,25 @@ typedef struct RingbufferDefinition {
uint8_t *pucTail; //Pointer to the end of the ring buffer storage area
BaseType_t xItemsWaiting; //Number of items/bytes(for byte buffers) currently in ring buffer that have not yet been read
/*
* TransSem: Binary semaphore used to indicate to a blocked transmitting tasks
* that more free space has become available or that the block has
* timed out.
*
* RecvSem: Binary semaphore used to indicate to a blocked receiving task that
* new data/item has been written to the ring buffer.
*
* Note - When static allocation is enabled, the two semaphores are always
* statically stored in the ring buffer's control structure
* regardless of whether the ring buffer is allocated dynamically or
* statically. When static allocation is disabled, the two semaphores
* are allocated dynamically and their handles stored instead, thus
* making the ring buffer's control structure slightly smaller when
* static allocation is disabled.
*/
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
StaticSemaphore_t xTransSemStatic;
StaticSemaphore_t xRecvSemStatic;
#else
SemaphoreHandle_t xTransSemHandle;
SemaphoreHandle_t xRecvSemHandle;
#endif
List_t xTasksWaitingToSend; //List of tasks that are blocked waiting to send/acquire onto this ring buffer. Stored in priority order.
List_t xTasksWaitingToReceive; //List of tasks that are blocked waiting to receive from this ring buffer. Stored in priority order.
QueueSetHandle_t xQueueSet; //Ring buffer's read queue set handle.
portMUX_TYPE mux; //Spinlock required for SMP
} Ringbuffer_t;
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
#if __GNUC_PREREQ(4, 6)
_Static_assert(sizeof(StaticRingbuffer_t) == sizeof(Ringbuffer_t), "StaticRingbuffer_t != Ringbuffer_t");
#endif
#endif
/*
Remark: A counting semaphore for items_buffered_sem would be more logical, but counting semaphores in
FreeRTOS need a maximum count, and allocate more memory the larger the maximum count is. Here, we
would need to set the maximum to the maximum amount of times a null-byte unit first in the buffer,
which is quite high and so would waste a fair amount of memory.
*/
/* --------------------------- Static Declarations -------------------------- */
// ------------------------------------------------ Forward Declares ---------------------------------------------------
/*
* WARNING: All of the following static functions (except generic functions)
* ARE NOT THREAD SAFE. Therefore they should only be called within a critical
* section (using spin locks)
*/
//Initialize a ring buffer after space has been allocated for it
static void prvInitializeNewRingbuffer(size_t xBufferSize,
RingbufferType_t xBufferType,
@ -201,12 +169,23 @@ static size_t prvGetCurMaxSizeAllowSplit(Ringbuffer_t *pxRingbuffer);
//Get the maximum size an item that can currently have if sent to a byte buffer
static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer);
/**
* Generic function used to retrieve an item/data from ring buffers. If called on
* an allow-split buffer, and pvItem2 and xItemSize2 are not NULL, both parts of
* a split item will be retrieved. xMaxSize will only take effect if called on
* byte buffers.
*/
/*
Generic function used to send or acquire an item/buffer.
- If sending, set ppvItem to NULL. pvItem remains unchanged on failure.
- If acquiring, set pvItem to NULL. ppvItem remains unchanged on failure.
*/
static BaseType_t prvSendAcquireGeneric(Ringbuffer_t *pxRingbuffer,
const void *pvItem,
void **ppvItem,
size_t xItemSize,
TickType_t xTicksToWait);
/*
Generic function used to retrieve an item/data from ring buffers. If called on
an allow-split buffer, and pvItem2 and xItemSize2 are not NULL, both parts of
a split item will be retrieved. xMaxSize will only take effect if called on
byte buffers. xItemSize must remain unchanged if no item is retrieved.
*/
static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
void **pvItem1,
void **pvItem2,
@ -215,7 +194,7 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
size_t xMaxSize,
TickType_t xTicksToWait);
//Generic function used to retrieve an item/data from ring buffers in an ISR
//From ISR version of prvReceiveGeneric()
static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
void **pvItem1,
void **pvItem2,
@ -223,7 +202,7 @@ static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
size_t *xItemSize2,
size_t xMaxSize);
/* --------------------------- Static Definitions --------------------------- */
// ------------------------------------------------ Static Functions ---------------------------------------------------
static void prvInitializeNewRingbuffer(size_t xBufferSize,
RingbufferType_t xBufferType,
@ -272,7 +251,11 @@ static void prvInitializeNewRingbuffer(size_t xBufferSize,
pxNewRingbuffer->xMaxItemSize = pxNewRingbuffer->xSize;
pxNewRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeByteBuf;
}
xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxNewRingbuffer));
vListInitialise(&pxNewRingbuffer->xTasksWaitingToSend);
vListInitialise(&pxNewRingbuffer->xTasksWaitingToReceive);
pxNewRingbuffer->xQueueSet = NULL;
portMUX_INITIALIZE(&pxNewRingbuffer->mux);
}
@ -759,6 +742,73 @@ static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer)
return xFreeSize;
}
static BaseType_t prvSendAcquireGeneric(Ringbuffer_t *pxRingbuffer,
const void *pvItem,
void **ppvItem,
size_t xItemSize,
TickType_t xTicksToWait)
{
BaseType_t xReturn = pdFALSE;
BaseType_t xExitLoop = pdFALSE;
BaseType_t xEntryTimeSet = pdFALSE;
BaseType_t xNotifyQueueSet = pdFALSE;
TimeOut_t xTimeOut;
while (xExitLoop == pdFALSE) {
portENTER_CRITICAL(&pxRingbuffer->mux);
if (pxRingbuffer->xCheckItemFits(pxRingbuffer, xItemSize) == pdTRUE) {
//xItemSize will fit. Copy or acquire the buffer immediately
if (ppvItem) {
//Acquire the buffer
*ppvItem = prvAcquireItemNoSplit(pxRingbuffer, xItemSize);
} else {
//Copy item into buffer
pxRingbuffer->vCopyItem(pxRingbuffer, pvItem, xItemSize);
if (pxRingbuffer->xQueueSet) {
//If ring buffer was added to a queue set, notify the queue set
xNotifyQueueSet = pdTRUE;
} else {
//If a task was waiting for data to arrive on the ring buffer, unblock it immediately.
if (listLIST_IS_EMPTY(&pxRingbuffer->xTasksWaitingToReceive) == pdFALSE) {
if (xTaskRemoveFromEventList(&pxRingbuffer->xTasksWaitingToReceive) == pdTRUE) {
//The unblocked task will preempt us. Trigger a yield here.
portYIELD_WITHIN_API();
}
}
}
}
xReturn = pdTRUE;
xExitLoop = pdTRUE;
goto loop_end;
} else if (xTicksToWait == (TickType_t) 0) {
//No block time. Return immediately.
xExitLoop = pdTRUE;
goto loop_end;
} else if (xEntryTimeSet == pdFALSE) {
//This is our first block. Set entry time
vTaskInternalSetTimeOutState(&xTimeOut);
xEntryTimeSet = pdTRUE;
}
if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE) {
//Not timed out yet. Block the current task
vTaskPlaceOnEventList(&pxRingbuffer->xTasksWaitingToSend, xTicksToWait);
portYIELD_WITHIN_API();
} else {
//We have timed out
xExitLoop = pdTRUE;
}
loop_end:
portEXIT_CRITICAL(&pxRingbuffer->mux);
}
//Defer notifying the queue set until we are outside the loop and critical section.
if (xNotifyQueueSet == pdTRUE) {
xQueueSend((QueueHandle_t)pxRingbuffer->xQueueSet, (QueueSetMemberHandle_t *)&pxRingbuffer, 0);
}
return xReturn;
}
static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
void **pvItem1,
void **pvItem2,
@ -768,30 +818,31 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
TickType_t xTicksToWait)
{
BaseType_t xReturn = pdFALSE;
BaseType_t xReturnSemaphore = pdFALSE;
TickType_t xTicksEnd = xTaskGetTickCount() + xTicksToWait;
TickType_t xTicksRemaining = xTicksToWait;
while (xTicksRemaining <= xTicksToWait) { //xTicksToWait will underflow once xTaskGetTickCount() > ticks_end
//Block until more free space becomes available or timeout
if (xSemaphoreTake(rbGET_RX_SEM_HANDLE(pxRingbuffer), xTicksRemaining) != pdTRUE) {
xReturn = pdFALSE; //Timed out attempting to get semaphore
break;
}
BaseType_t xExitLoop = pdFALSE;
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
//Semaphore obtained, check if item can be retrieved
#ifdef __clang_analyzer__
// Teach clang-tidy that if NULL pointers are provided, this function will never dereference them
if (!pvItem1 || !pvItem2 || !xItemSize1 || !xItemSize2) {
return pdFALSE;
}
#endif /*__clang_analyzer__ */
while (xExitLoop == pdFALSE) {
portENTER_CRITICAL(&pxRingbuffer->mux);
if (prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
//Item is available for retrieval
//Item/data is available for retrieval
BaseType_t xIsSplit = pdFALSE;
if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) {
//Second argument (pxIsSplit) is unused for byte buffers
//Read up to xMaxSize bytes from byte buffer
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, NULL, xMaxSize, xItemSize1);
} else {
//Third argument (xMaxSize) is unused for no-split/allow-split buffers
//Get (first) item from no-split/allow-split buffers
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize1);
}
//Check for item split if configured to do so
if ((pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) && (pvItem2 != NULL) && (xItemSize2 != NULL)) {
//If split buffer, check for split items
if (pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) {
if (xIsSplit == pdTRUE) {
*pvItem2 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize2);
configASSERT(*pvItem2 < *pvItem1); //Check wrap around has occurred
@ -801,26 +852,30 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
}
}
xReturn = pdTRUE;
if (pxRingbuffer->xItemsWaiting > 0) {
xReturnSemaphore = pdTRUE;
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
break;
xExitLoop = pdTRUE;
goto loop_end;
} else if (xTicksToWait == (TickType_t) 0) {
//No block time. Return immediately.
xExitLoop = pdTRUE;
goto loop_end;
} else if (xEntryTimeSet == pdFALSE) {
//This is our first block. Set entry time
vTaskInternalSetTimeOutState(&xTimeOut);
xEntryTimeSet = pdTRUE;
}
//No item available for retrieval, adjust ticks and take the semaphore again
if (xTicksToWait != portMAX_DELAY) {
xTicksRemaining = xTicksEnd - xTaskGetTickCount();
if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE) {
//Not timed out yet. Block the current task
vTaskPlaceOnEventList(&pxRingbuffer->xTasksWaitingToReceive, xTicksToWait);
portYIELD_WITHIN_API();
} else {
//We have timed out.
xExitLoop = pdTRUE;
}
loop_end:
portEXIT_CRITICAL(&pxRingbuffer->mux);
/*
* Gap between critical section and re-acquiring of the semaphore. If
* semaphore is given now, priority inversion might occur (see docs)
*/
}
if (xReturnSemaphore == pdTRUE) {
xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer)); //Give semaphore back so other tasks can retrieve
}
return xReturn;
}
@ -832,20 +887,26 @@ static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
size_t xMaxSize)
{
BaseType_t xReturn = pdFALSE;
BaseType_t xReturnSemaphore = pdFALSE;
#ifdef __clang_analyzer__
// Teach clang-tidy that if NULL pointers are provided, this function will never dereference them
if (!pvItem1 || !pvItem2 || !xItemSize1 || !xItemSize2) {
return pdFALSE;
}
#endif /*__clang_analyzer__ */
portENTER_CRITICAL_ISR(&pxRingbuffer->mux);
if(prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
if (prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
BaseType_t xIsSplit = pdFALSE;
if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) {
//Second argument (pxIsSplit) is unused for byte buffers
//Read up to xMaxSize bytes from byte buffer
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, NULL, xMaxSize, xItemSize1);
} else {
//Third argument (xMaxSize) is unused for no-split/allow-split buffers
//Get (first) item from no-split/allow-split buffers
*pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize1);
}
//Check for item split if configured to do so
if ((pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) && pvItem2 != NULL && xItemSize2 != NULL) {
//If split buffer, check for split items
if (pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) {
if (xIsSplit == pdTRUE) {
*pvItem2 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize2);
configASSERT(*pvItem2 < *pvItem1); //Check wrap around has occurred
@ -855,19 +916,15 @@ static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer,
}
}
xReturn = pdTRUE;
if (pxRingbuffer->xItemsWaiting > 0) {
xReturnSemaphore = pdTRUE;
}
} else {
xReturn = pdFALSE;
}
portEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
if (xReturnSemaphore == pdTRUE) {
xSemaphoreGiveFromISR(rbGET_RX_SEM_HANDLE(pxRingbuffer), NULL); //Give semaphore back so other tasks can retrieve
}
return xReturn;
}
/* --------------------------- Public Definitions --------------------------- */
// ------------------------------------------------ Public Functions ---------------------------------------------------
RingbufHandle_t xRingbufferCreate(size_t xBufferSize, RingbufferType_t xBufferType)
{
@ -884,25 +941,6 @@ RingbufHandle_t xRingbufferCreate(size_t xBufferSize, RingbufferType_t xBufferTy
goto err;
}
//Initialize Semaphores
#if ( configSUPPORT_STATIC_ALLOCATION == 1)
//We don't use the handles for static semaphores, and xSemaphoreCreateBinaryStatic will never fail thus no need to check static case
xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xTransSemStatic));
xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xRecvSemStatic));
#else
pxNewRingbuffer->xTransSemHandle = xSemaphoreCreateBinary();
pxNewRingbuffer->xRecvSemHandle = xSemaphoreCreateBinary();
if (pxNewRingbuffer->xTransSemHandle == NULL || pxNewRingbuffer->xRecvSemHandle == NULL) {
if (pxNewRingbuffer->xTransSemHandle != NULL) {
vSemaphoreDelete(pxNewRingbuffer->xTransSemHandle);
}
if (pxNewRingbuffer->xRecvSemHandle != NULL) {
vSemaphoreDelete(pxNewRingbuffer->xRecvSemHandle);
}
goto err;
}
#endif
prvInitializeNewRingbuffer(xBufferSize, xBufferType, pxNewRingbuffer, pucRingbufferStorage);
return (RingbufHandle_t)pxNewRingbuffer;
@ -918,7 +956,6 @@ RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum)
return xRingbufferCreate((rbALIGN_SIZE(xItemSize) + rbHEADER_SIZE) * xItemNum, RINGBUF_TYPE_NOSPLIT);
}
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize,
RingbufferType_t xBufferType,
uint8_t *pucRingbufferStorage,
@ -934,22 +971,19 @@ RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize,
}
Ringbuffer_t *pxNewRingbuffer = (Ringbuffer_t *)pxStaticRingbuffer;
xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xTransSemStatic));
xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xRecvSemStatic));
prvInitializeNewRingbuffer(xBufferSize, xBufferType, pxNewRingbuffer, pucRingbufferStorage);
pxNewRingbuffer->uxRingbufferFlags |= rbBUFFER_STATIC_FLAG;
return (RingbufHandle_t)pxNewRingbuffer;
}
#endif
BaseType_t xRingbufferSendAcquire(RingbufHandle_t xRingbuffer, void **ppvItem, size_t xItemSize, TickType_t xTicksToWait)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
//Check arguments
configASSERT(pxRingbuffer);
configASSERT(ppvItem != NULL || xItemSize == 0);
//currently only supported in NoSplit buffers
configASSERT((pxRingbuffer->uxRingbufferFlags & (rbBYTE_BUFFER_FLAG | rbALLOW_SPLIT_FLAG)) == 0);
configASSERT(ppvItem != NULL);
configASSERT((pxRingbuffer->uxRingbufferFlags & (rbBYTE_BUFFER_FLAG | rbALLOW_SPLIT_FLAG)) == 0); //Send acquire currently only supported in NoSplit buffers
*ppvItem = NULL;
if (xItemSize > pxRingbuffer->xMaxItemSize) {
@ -959,61 +993,38 @@ BaseType_t xRingbufferSendAcquire(RingbufHandle_t xRingbuffer, void **ppvItem, s
return pdTRUE; //Sending 0 bytes to byte buffer has no effect
}
//Attempt to send an item
BaseType_t xReturn = pdFALSE;
BaseType_t xReturnSemaphore = pdFALSE;
TickType_t xTicksEnd = xTaskGetTickCount() + xTicksToWait;
TickType_t xTicksRemaining = xTicksToWait;
while (xTicksRemaining <= xTicksToWait) { //xTicksToWait will underflow once xTaskGetTickCount() > ticks_end
//Block until more free space becomes available or timeout
if (xSemaphoreTake(rbGET_TX_SEM_HANDLE(pxRingbuffer), xTicksRemaining) != pdTRUE) {
xReturn = pdFALSE;
break;
}
//Semaphore obtained, check if item can fit
portENTER_CRITICAL(&pxRingbuffer->mux);
if(pxRingbuffer->xCheckItemFits(pxRingbuffer, xItemSize) == pdTRUE) {
//Item will fit, copy item
*ppvItem = prvAcquireItemNoSplit(pxRingbuffer, xItemSize);
xReturn = pdTRUE;
//Check if the free semaphore should be returned to allow other tasks to send
if (prvGetFreeSize(pxRingbuffer) > 0) {
xReturnSemaphore = pdTRUE;
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
break;
}
//Item doesn't fit, adjust ticks and take the semaphore again
if (xTicksToWait != portMAX_DELAY) {
xTicksRemaining = xTicksEnd - xTaskGetTickCount();
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
/*
* Gap between critical section and re-acquiring of the semaphore. If
* semaphore is given now, priority inversion might occur (see docs)
*/
}
if (xReturnSemaphore == pdTRUE) {
xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxRingbuffer)); //Give back semaphore so other tasks can acquire
}
return xReturn;
return prvSendAcquireGeneric(pxRingbuffer, NULL, ppvItem, xItemSize, xTicksToWait);
}
BaseType_t xRingbufferSendComplete(RingbufHandle_t xRingbuffer, void *pvItem)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
BaseType_t xNotifyQueueSet = pdFALSE;
//Check arguments
configASSERT(pxRingbuffer);
configASSERT(pvItem != NULL);
configASSERT((pxRingbuffer->uxRingbufferFlags & (rbBYTE_BUFFER_FLAG | rbALLOW_SPLIT_FLAG)) == 0);
portENTER_CRITICAL(&pxRingbuffer->mux);
prvSendItemDoneNoSplit(pxRingbuffer, pvItem);
if (pxRingbuffer->xQueueSet) {
//If ring buffer was added to a queue set, notify the queue set
xNotifyQueueSet = pdTRUE;
} else {
//If a task was waiting for data to arrive on the ring buffer, unblock it immediately.
if (listLIST_IS_EMPTY(&pxRingbuffer->xTasksWaitingToReceive) == pdFALSE) {
if (xTaskRemoveFromEventList(&pxRingbuffer->xTasksWaitingToReceive) == pdTRUE) {
//The unblocked task will preempt us. Trigger a yield here.
portYIELD_WITHIN_API();
}
}
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer));
if (xNotifyQueueSet == pdTRUE) {
xQueueSend((QueueHandle_t)pxRingbuffer->xQueueSet, (QueueSetMemberHandle_t *)&pxRingbuffer, 0);
}
return pdTRUE;
}
@ -1022,8 +1033,9 @@ BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer,
size_t xItemSize,
TickType_t xTicksToWait)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
//Check arguments
configASSERT(pxRingbuffer);
configASSERT(pvItem != NULL || xItemSize == 0);
if (xItemSize > pxRingbuffer->xMaxItemSize) {
@ -1033,49 +1045,7 @@ BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer,
return pdTRUE; //Sending 0 bytes to byte buffer has no effect
}
//Attempt to send an item
BaseType_t xReturn = pdFALSE;
BaseType_t xReturnSemaphore = pdFALSE;
TickType_t xTicksEnd = xTaskGetTickCount() + xTicksToWait;
TickType_t xTicksRemaining = xTicksToWait;
while (xTicksRemaining <= xTicksToWait) { //xTicksToWait will underflow once xTaskGetTickCount() > ticks_end
//Block until more free space becomes available or timeout
if (xSemaphoreTake(rbGET_TX_SEM_HANDLE(pxRingbuffer), xTicksRemaining) != pdTRUE) {
xReturn = pdFALSE;
break;
}
//Semaphore obtained, check if item can fit
portENTER_CRITICAL(&pxRingbuffer->mux);
if(pxRingbuffer->xCheckItemFits(pxRingbuffer, xItemSize) == pdTRUE) {
//Item will fit, copy item
pxRingbuffer->vCopyItem(pxRingbuffer, pvItem, xItemSize);
xReturn = pdTRUE;
//Check if the free semaphore should be returned to allow other tasks to send
if (prvGetFreeSize(pxRingbuffer) > 0) {
xReturnSemaphore = pdTRUE;
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
break;
}
//Item doesn't fit, adjust ticks and take the semaphore again
if (xTicksToWait != portMAX_DELAY) {
xTicksRemaining = xTicksEnd - xTaskGetTickCount();
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
/*
* Gap between critical section and re-acquiring of the semaphore. If
* semaphore is given now, priority inversion might occur (see docs)
*/
}
if (xReturnSemaphore == pdTRUE) {
xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxRingbuffer)); //Give back semaphore so other tasks can send
}
if (xReturn == pdTRUE) {
//Indicate item was successfully sent
xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer));
}
return xReturn;
return prvSendAcquireGeneric(pxRingbuffer, pvItem, NULL, xItemSize, xTicksToWait);
}
BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer,
@ -1083,8 +1053,11 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer,
size_t xItemSize,
BaseType_t *pxHigherPriorityTaskWoken)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
BaseType_t xNotifyQueueSet = pdFALSE;
BaseType_t xReturn;
//Check arguments
configASSERT(pxRingbuffer);
configASSERT(pvItem != NULL || xItemSize == 0);
if (xItemSize > pxRingbuffer->xMaxItemSize) {
@ -1094,45 +1067,45 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer,
return pdTRUE; //Sending 0 bytes to byte buffer has no effect
}
//Attempt to send an item
BaseType_t xReturn;
BaseType_t xReturnSemaphore = pdFALSE;
portENTER_CRITICAL_ISR(&pxRingbuffer->mux);
if (pxRingbuffer->xCheckItemFits(xRingbuffer, xItemSize) == pdTRUE) {
pxRingbuffer->vCopyItem(xRingbuffer, pvItem, xItemSize);
xReturn = pdTRUE;
//Check if the free semaphore should be returned to allow other tasks to send
if (prvGetFreeSize(pxRingbuffer) > 0) {
xReturnSemaphore = pdTRUE;
if (pxRingbuffer->xQueueSet) {
//If ring buffer was added to a queue set, notify the queue set
xNotifyQueueSet = pdTRUE;
} else {
//If a task was waiting for data to arrive on the ring buffer, unblock it immediately.
if (listLIST_IS_EMPTY(&pxRingbuffer->xTasksWaitingToReceive) == pdFALSE) {
if (xTaskRemoveFromEventList(&pxRingbuffer->xTasksWaitingToReceive) == pdTRUE) {
//The unblocked task will preempt us. Record that a context switch is required.
if (pxHigherPriorityTaskWoken != NULL) {
*pxHigherPriorityTaskWoken = pdTRUE;
}
}
}
}
xReturn = pdTRUE;
} else {
xReturn = pdFALSE;
}
portEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
if (xReturnSemaphore == pdTRUE) {
xSemaphoreGiveFromISR(rbGET_TX_SEM_HANDLE(pxRingbuffer), pxHigherPriorityTaskWoken); //Give back semaphore so other tasks can send
}
if (xReturn == pdTRUE) {
//Indicate item was successfully sent
xSemaphoreGiveFromISR(rbGET_RX_SEM_HANDLE(pxRingbuffer), pxHigherPriorityTaskWoken);
//Defer notifying the queue set until we are outside the critical section.
if (xNotifyQueueSet == pdTRUE) {
xQueueSendFromISR((QueueHandle_t)pxRingbuffer->xQueueSet, (QueueSetMemberHandle_t *)&pxRingbuffer, pxHigherPriorityTaskWoken);
}
return xReturn;
}
void *xRingbufferReceive(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
//Check arguments
configASSERT(pxRingbuffer && pxItemSize);
//Attempt to retrieve an item
void *pvTempItem;
size_t xTempSize;
if (prvReceiveGeneric(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, 0, xTicksToWait) == pdTRUE) {
if (pxItemSize != NULL) {
*pxItemSize = xTempSize;
}
if (prvReceiveGeneric(pxRingbuffer, &pvTempItem, NULL, pxItemSize, NULL, 0, xTicksToWait) == pdTRUE) {
return pvTempItem;
} else {
return NULL;
@ -1141,17 +1114,14 @@ void *xRingbufferReceive(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickTy
void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
//Check arguments
configASSERT(pxRingbuffer && pxItemSize);
//Attempt to retrieve an item
void *pvTempItem;
size_t xTempSize;
if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, 0) == pdTRUE) {
if (pxItemSize != NULL) {
*pxItemSize = xTempSize;
}
if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempItem, NULL, pxItemSize, NULL, 0) == pdTRUE) {
return pvTempItem;
} else {
return NULL;
@ -1165,37 +1135,13 @@ BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer,
size_t *pxTailItemSize,
TickType_t xTicksToWait)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
configASSERT(pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG);
configASSERT(ppvHeadItem != NULL && ppvTailItem != NULL);
//Attempt to retrieve multiple items
void *pvTempHeadItem, *pvTempTailItem;
size_t xTempHeadSize, xTempTailSize;
if (prvReceiveGeneric(pxRingbuffer, &pvTempHeadItem, &pvTempTailItem, &xTempHeadSize, &xTempTailSize, 0, xTicksToWait) == pdTRUE) {
//At least one item was retrieved
*ppvHeadItem = pvTempHeadItem;
if(pxHeadItemSize != NULL){
*pxHeadItemSize = xTempHeadSize;
}
//Check to see if a second item was also retrieved
if (pvTempTailItem != NULL) {
*ppvTailItem = pvTempTailItem;
if (pxTailItemSize != NULL) {
*pxTailItemSize = xTempTailSize;
}
} else {
*ppvTailItem = NULL;
}
return pdTRUE;
} else {
//No items retrieved
*ppvHeadItem = NULL;
*ppvTailItem = NULL;
return pdFALSE;
}
//Check arguments
configASSERT(pxRingbuffer && ppvHeadItem && ppvTailItem && pxHeadItemSize && pxTailItemSize);
configASSERT(pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG);
return prvReceiveGeneric(pxRingbuffer, ppvHeadItem, ppvTailItem, pxHeadItemSize, pxTailItemSize, 0, xTicksToWait);
}
BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer,
@ -1204,36 +1150,13 @@ BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer,
size_t *pxHeadItemSize,
size_t *pxTailItemSize)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
configASSERT(pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG);
configASSERT(ppvHeadItem != NULL && ppvTailItem != NULL);
//Attempt to retrieve multiple items
void *pvTempHeadItem = NULL, *pvTempTailItem = NULL;
size_t xTempHeadSize, xTempTailSize;
if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempHeadItem, &pvTempTailItem, &xTempHeadSize, &xTempTailSize, 0) == pdTRUE) {
//At least one item was received
*ppvHeadItem = pvTempHeadItem;
if (pxHeadItemSize != NULL) {
*pxHeadItemSize = xTempHeadSize;
}
//Check to see if a second item was also retrieved
if (pvTempTailItem != NULL) {
*ppvTailItem = pvTempTailItem;
if (pxTailItemSize != NULL) {
*pxTailItemSize = xTempTailSize;
}
} else {
*ppvTailItem = NULL;
}
return pdTRUE;
} else {
*ppvHeadItem = NULL;
*ppvTailItem = NULL;
return pdFALSE;
}
//Check arguments
configASSERT(pxRingbuffer && ppvHeadItem && ppvTailItem && pxHeadItemSize && pxTailItemSize);
configASSERT(pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG);
return prvReceiveGenericFromISR(pxRingbuffer, ppvHeadItem, ppvTailItem, pxHeadItemSize, pxTailItemSize, 0);
}
void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer,
@ -1241,21 +1164,18 @@ void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer,
TickType_t xTicksToWait,
size_t xMaxSize)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
//Check arguments
configASSERT(pxRingbuffer && pxItemSize);
configASSERT(pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG); //This function should only be called for byte buffers
if (xMaxSize == 0) {
return NULL;
}
//Attempt to retrieve up to xMaxSize bytes
void *pvTempItem;
size_t xTempSize;
if (prvReceiveGeneric(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, xMaxSize, xTicksToWait) == pdTRUE) {
if (pxItemSize != NULL) {
*pxItemSize = xTempSize;
}
if (prvReceiveGeneric(pxRingbuffer, &pvTempItem, NULL, pxItemSize, NULL, xMaxSize, xTicksToWait) == pdTRUE) {
return pvTempItem;
} else {
return NULL;
@ -1264,21 +1184,18 @@ void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer,
void *xRingbufferReceiveUpToFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize, size_t xMaxSize)
{
//Check arguments
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
//Check arguments
configASSERT(pxRingbuffer && pxItemSize);
configASSERT(pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG); //This function should only be called for byte buffers
if (xMaxSize == 0) {
return NULL;
}
//Attempt to retrieve up to xMaxSize bytes
void *pvTempItem;
size_t xTempSize;
if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, xMaxSize) == pdTRUE) {
if (pxItemSize != NULL) {
*pxItemSize = xTempSize;
}
if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempItem, NULL, pxItemSize, NULL, xMaxSize) == pdTRUE) {
return pvTempItem;
} else {
return NULL;
@ -1293,8 +1210,14 @@ void vRingbufferReturnItem(RingbufHandle_t xRingbuffer, void *pvItem)
portENTER_CRITICAL(&pxRingbuffer->mux);
pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem);
//If a task was waiting for space to send, unblock it immediately.
if (listLIST_IS_EMPTY(&pxRingbuffer->xTasksWaitingToSend) == pdFALSE) {
if (xTaskRemoveFromEventList(&pxRingbuffer->xTasksWaitingToSend) == pdTRUE) {
//The unblocked task will preempt us. Trigger a yield here.
portYIELD_WITHIN_API();
}
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxRingbuffer));
}
void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, BaseType_t *pxHigherPriorityTaskWoken)
@ -1305,8 +1228,16 @@ void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, Bas
portENTER_CRITICAL_ISR(&pxRingbuffer->mux);
pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem);
//If a task was waiting for space to send, unblock it immediately.
if (listLIST_IS_EMPTY(&pxRingbuffer->xTasksWaitingToSend) == pdFALSE) {
if (xTaskRemoveFromEventList(&pxRingbuffer->xTasksWaitingToSend) == pdTRUE) {
//The unblocked task will preempt us. Record that a context switch is required.
if (pxHigherPriorityTaskWoken != NULL) {
*pxHigherPriorityTaskWoken = pdTRUE;
}
}
}
portEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
xSemaphoreGiveFromISR(rbGET_TX_SEM_HANDLE(pxRingbuffer), pxHigherPriorityTaskWoken);
}
void vRingbufferDelete(RingbufHandle_t xRingbuffer)
@ -1314,9 +1245,6 @@ void vRingbufferDelete(RingbufHandle_t xRingbuffer)
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
vSemaphoreDelete(rbGET_TX_SEM_HANDLE(pxRingbuffer));
vSemaphoreDelete(rbGET_RX_SEM_HANDLE(pxRingbuffer));
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_STATIC_FLAG) {
//Ring buffer was statically allocated, no need to free
@ -1349,46 +1277,48 @@ size_t xRingbufferGetCurFreeSize(RingbufHandle_t xRingbuffer)
BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
{
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
BaseType_t xReturn;
configASSERT(pxRingbuffer && xQueueSet);
portENTER_CRITICAL(&pxRingbuffer->mux);
//Cannot add semaphore to queue set if semaphore is not empty. Temporarily hold semaphore
BaseType_t result = xSemaphoreTake(rbGET_RX_SEM_HANDLE(pxRingbuffer), 0);
xReturn = xQueueAddToSet(rbGET_RX_SEM_HANDLE(pxRingbuffer), xQueueSet);
if (result == pdTRUE) {
//Return semaphore if temporarily held
result = xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer));
configASSERT(result == pdTRUE);
if (pxRingbuffer->xQueueSet != NULL || prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
/*
- Cannot add ring buffer to more than one queue set
- It is dangerous to add a ring buffer to a queue set if the ring buffer currently has data to be read.
*/
xReturn = pdFALSE;
} else {
//Add ring buffer to queue set
pxRingbuffer->xQueueSet = xQueueSet;
xReturn = pdTRUE;
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
return xReturn;
}
BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_t xMember)
{
//Check if the selected queue set member is the ring buffer's read semaphore
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
return (rbGET_RX_SEM_HANDLE(pxRingbuffer) == xMember) ? pdTRUE : pdFALSE;
return xReturn;
}
BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
{
Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
configASSERT(pxRingbuffer);
BaseType_t xReturn;
configASSERT(pxRingbuffer && xQueueSet);
portENTER_CRITICAL(&pxRingbuffer->mux);
//Cannot remove semaphore from queue set if semaphore is not empty. Temporarily hold semaphore
BaseType_t result = xSemaphoreTake(rbGET_RX_SEM_HANDLE(pxRingbuffer), 0);
xReturn = xQueueRemoveFromSet(rbGET_RX_SEM_HANDLE(pxRingbuffer), xQueueSet);
if (result == pdTRUE) {
//Return semaphore if temporarily held
result = xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer));
configASSERT(result == pdTRUE);
if (pxRingbuffer->xQueueSet != xQueueSet || prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
/*
- Ring buffer was never added to this queue set
- It is dangerous to remove a ring buffer from a queue set if the ring buffer currently has data to be read.
*/
xReturn = pdFALSE;
} else {
//Remove ring buffer from queue set
pxRingbuffer->xQueueSet = NULL;
xReturn = pdTRUE;
}
portEXIT_CRITICAL(&pxRingbuffer->mux);
return xReturn;
}

Wyświetl plik

@ -1046,3 +1046,49 @@ TEST_CASE("Test ringbuffer functions work with flash cache disabled", "[esp_ring
TEST_ASSERT( iram_ringbuf_test() );
}
#endif /* !CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH && !CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH */
/* ------------------------ Test ring buffer 0 Item Size -----------------------
* The following test case tests that sending/acquiring an item/bytes of 0 size
* is permissible.
*/
TEST_CASE("Test ringbuffer 0 item size", "[esp_ringbuf]")
{
RingbufHandle_t no_split_rb = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT);
RingbufHandle_t allow_split_rb = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_ALLOWSPLIT);
RingbufHandle_t byte_rb = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_BYTEBUF);
TEST_ASSERT_MESSAGE(no_split_rb && allow_split_rb && byte_rb, "Failed to create ring buffers");
void *pvItem1;
void *pvItem2;
size_t xItemSize1;
size_t xItemSize2;
//Test that 0 item size on no split buffers should only send a header with no data
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSend(no_split_rb, NULL, 0, 0));
TEST_ASSERT_NOT_EQUAL(NULL, xRingbufferReceive(no_split_rb, &xItemSize1, 0));
TEST_ASSERT_EQUAL(0, xItemSize1);
//Test that acquiring 0 item size on no split buffers should only send a header without reserving a data buffer
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSendAcquire(no_split_rb, &pvItem1, 0, 0));
TEST_ASSERT_NOT_EQUAL(NULL, pvItem1);
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSendComplete(no_split_rb, pvItem1));
TEST_ASSERT_NOT_EQUAL(NULL, xRingbufferReceive(no_split_rb, &xItemSize1, 0));
TEST_ASSERT_EQUAL(0, xItemSize1);
//Test that 0 item size on allow split buffers should only send a header with no data
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSend(allow_split_rb, NULL, 0, 0));
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferReceiveSplit(allow_split_rb, &pvItem1, &pvItem2, &xItemSize1, &xItemSize2, 0));
TEST_ASSERT_NOT_EQUAL(NULL, pvItem1);
TEST_ASSERT_EQUAL(NULL, pvItem2);
TEST_ASSERT_EQUAL(0, xItemSize1);
//Test that 0 item size on byte buffers should send nothing
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSend(byte_rb, NULL, 0, 0));
TEST_ASSERT_EQUAL(pdFALSE, xRingbufferReceiveUpTo(byte_rb, &xItemSize1, 0, BUFFER_SIZE));
//Cleanup
vRingbufferDelete(no_split_rb);
vRingbufferDelete(allow_split_rb);
vRingbufferDelete(byte_rb);
}

Wyświetl plik

@ -368,17 +368,6 @@ The code snippet below demonstrates a ring buffer being allocated entirely in ex
free(buffer_struct);
free(buffer_storage);
Priority Inversion
^^^^^^^^^^^^^^^^^^
Ideally, ring buffers can be used with multiple tasks in an SMP fashion where the **highest priority task will always be serviced first.** However due to the usage of binary semaphores in the ring buffer's underlying implementation, priority inversion may occur under very specific circumstances.
The ring buffer governs sending by a binary semaphore which is given whenever space is freed on the ring buffer. The highest priority task waiting to send will repeatedly take the semaphore until sufficient free space becomes available or until it times out. Ideally this should prevent any lower priority tasks from being serviced as the semaphore should always be given to the highest priority task.
However, in between iterations of acquiring the semaphore, there is a **gap in the critical section** which may permit another task (on the other core or with an even higher priority) to free some space on the ring buffer and as a result give the semaphore. Therefore, the semaphore will be given before the highest priority task can re-acquire the semaphore. This will result in the **semaphore being acquired by the second-highest priority task** waiting to send, hence causing priority inversion.
This side effect will not affect ring buffer performance drastically given if the number of tasks using the ring buffer simultaneously is low, and the ring buffer is not operating near maximum capacity.
.. ------------------------------------------- ESP-IDF Tick and Idle Hooks ---------------------------------------------