mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
esp_ringbuf: Replace internal semaphores with task event lists
This commit replaces the existing TX/RX semaphores with FreeRTOS task event lists (similar to how FreeRTOS queues implement task blocking). Using task event lists allows the ring buffer object to be smaller and various ring buffer functions to be faster. The ring buffer queue set impelementation was also updated as a result.
This commit is contained in:
parent
df13918c80
commit
443d87c2ff
@ -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
|
||||
@ -429,14 +422,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 +438,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 +502,3 @@ void xRingbufferPrintInfo(RingbufHandle_t xRingbuffer);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FREERTOS_RINGBUF_H */
|
||||
|
@ -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)
|
||||
|
@ -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((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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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 ---------------------------------------------
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user