Skip to content

Commit cc132c6

Browse files
committed
fix(esp_ringbuf): Fixed no split buffer in case of wrap around with dummy data
1 parent f420609 commit cc132c6

File tree

2 files changed

+118
-26
lines changed

2 files changed

+118
-26
lines changed

components/esp_ringbuf/ringbuf.c

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,7 @@ static void prvCopyItemAllowSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *puc
437437
xItemSize -= xRemLen;
438438
xAlignedItemSize -= xRemLen;
439439
pxFirstHeader->uxItemFlags |= rbITEM_SPLIT_FLAG; //There must be more data
440+
pxFirstHeader->uxItemFlags |= rbITEM_WRITTEN_FLAG;
440441
} else {
441442
//Remaining length was only large enough to fit header
442443
pxFirstHeader->uxItemFlags |= rbITEM_DUMMY_DATA_FLAG; //Item will completely be stored in 2nd part
@@ -450,6 +451,7 @@ static void prvCopyItemAllowSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *puc
450451
pxSecondHeader->uxItemFlags = 0;
451452
pxRingbuffer->pucAcquire += rbHEADER_SIZE; //Advance acquire pointer past header
452453
memcpy(pxRingbuffer->pucAcquire, pucItem, xItemSize);
454+
pxSecondHeader->uxItemFlags |= rbITEM_WRITTEN_FLAG;
453455
pxRingbuffer->xItemsWaiting++;
454456
pxRingbuffer->pucAcquire += xAlignedItemSize; //Advance pucAcquire past item to next aligned address
455457

@@ -506,13 +508,6 @@ static BaseType_t prvCheckItemAvail(Ringbuffer_t *pxRingbuffer)
506508
return pdFALSE; //Byte buffers do not allow multiple retrievals before return
507509
}
508510
if ((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG))) {
509-
// If the ring buffer is a no-split buffer, the read pointer must point to an item that has been written to.
510-
if ((pxRingbuffer->uxRingbufferFlags & (rbBYTE_BUFFER_FLAG | rbALLOW_SPLIT_FLAG)) == 0) {
511-
ItemHeader_t *pxHeader = (ItemHeader_t *)pxRingbuffer->pucRead;
512-
if ((pxHeader->uxItemFlags & rbITEM_WRITTEN_FLAG) == 0) {
513-
return pdFALSE;
514-
}
515-
}
516511
return pdTRUE; //Items/data available for retrieval
517512
} else {
518513
return pdFALSE; //No items/data available for retrieval
@@ -540,22 +535,30 @@ static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer,
540535
pxHeader = (ItemHeader_t *)pxRingbuffer->pucRead;
541536
configASSERT(pxHeader->xItemLen <= pxRingbuffer->xMaxItemSize);
542537
}
543-
pcReturn = pxRingbuffer->pucRead + rbHEADER_SIZE; //Get pointer to part of item containing data (point past the header)
544-
if (pxHeader->xItemLen == 0) {
545-
//Inclusive of pucTail for special case where item of zero length just fits at the end of the buffer
546-
configASSERT(pcReturn >= pxRingbuffer->pucHead && pcReturn <= pxRingbuffer->pucTail);
547-
} else {
548-
//Exclusive of pucTail if length is larger than zero, pcReturn should never point to pucTail
549-
configASSERT(pcReturn >= pxRingbuffer->pucHead && pcReturn < pxRingbuffer->pucTail);
550-
}
551-
*pxItemSize = pxHeader->xItemLen; //Get length of item
552-
pxRingbuffer->xItemsWaiting --; //Update item count
553-
*pxIsSplit = (pxHeader->uxItemFlags & rbITEM_SPLIT_FLAG) ? pdTRUE : pdFALSE;
554538

555-
pxRingbuffer->pucRead += rbHEADER_SIZE + rbALIGN_SIZE(pxHeader->xItemLen); //Update pucRead
556-
//Check if pucRead requires wrap around
557-
if ((pxRingbuffer->pucTail - pxRingbuffer->pucRead) < rbHEADER_SIZE) {
558-
pxRingbuffer->pucRead = pxRingbuffer->pucHead;
539+
//In case of wrap around data might not be ready
540+
if ((pxHeader->uxItemFlags & rbITEM_WRITTEN_FLAG) == 0) {
541+
*pxIsSplit = pdFALSE;
542+
*pxItemSize = 0;
543+
pcReturn = NULL;
544+
} else {
545+
pcReturn = pxRingbuffer->pucRead + rbHEADER_SIZE; //Get pointer to part of item containing data (point past the header)
546+
if (pxHeader->xItemLen == 0) {
547+
//Inclusive of pucTail for special case where item of zero length just fits at the end of the buffer
548+
configASSERT(pcReturn >= pxRingbuffer->pucHead && pcReturn <= pxRingbuffer->pucTail);
549+
} else {
550+
//Exclusive of pucTail if length is larger than zero, pcReturn should never point to pucTail
551+
configASSERT(pcReturn >= pxRingbuffer->pucHead && pcReturn < pxRingbuffer->pucTail);
552+
}
553+
*pxItemSize = pxHeader->xItemLen; //Get length of item
554+
pxRingbuffer->xItemsWaiting --; //Update item count
555+
*pxIsSplit = (pxHeader->uxItemFlags & rbITEM_SPLIT_FLAG) ? pdTRUE : pdFALSE;
556+
557+
pxRingbuffer->pucRead += rbHEADER_SIZE + rbALIGN_SIZE(pxHeader->xItemLen); //Update pucRead
558+
//Check if pucRead requires wrap around
559+
if ((pxRingbuffer->pucTail - pxRingbuffer->pucRead) < rbHEADER_SIZE) {
560+
pxRingbuffer->pucRead = pxRingbuffer->pucHead;
561+
}
559562
}
560563
return (void *)pcReturn;
561564
}
@@ -827,13 +830,14 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
827830
BaseType_t xReturn = pdFALSE;
828831
BaseType_t xExitLoop = pdFALSE;
829832
BaseType_t xEntryTimeSet = pdFALSE;
833+
BaseType_t xSkipCheckAvail = pdFALSE;
830834
TimeOut_t xTimeOut;
831835

832836
ESP_STATIC_ANALYZER_CHECK(!pvItem1 || !pvItem2 || !xItemSize1 || !xItemSize2, pdFALSE);
833837

834838
while (xExitLoop == pdFALSE) {
835839
portENTER_CRITICAL(&pxRingbuffer->mux);
836-
if (prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
840+
if ((!xSkipCheckAvail) && prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
837841
//Item/data is available for retrieval
838842
BaseType_t xIsSplit = pdFALSE;
839843
if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) {
@@ -853,9 +857,15 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
853857
*pvItem2 = NULL;
854858
}
855859
}
856-
xReturn = pdTRUE;
857-
xExitLoop = pdTRUE;
858-
goto loop_end;
860+
861+
if (*pvItem1 == NULL) {
862+
xSkipCheckAvail = pdTRUE;
863+
goto loop_end;
864+
} else {
865+
xReturn = pdTRUE;
866+
xExitLoop = pdTRUE;
867+
goto loop_end;
868+
}
859869
} else if (xTicksToWait == (TickType_t) 0) {
860870
//No block time. Return immediately.
861871
xExitLoop = pdTRUE;
@@ -868,6 +878,7 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer,
868878

869879
if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE) {
870880
//Not timed out yet. Block the current task
881+
xSkipCheckAvail = pdFALSE;
871882
vTaskPlaceOnEventList(&pxRingbuffer->xTasksWaitingToReceive, xTicksToWait);
872883
portYIELD_WITHIN_API();
873884
} else {

components/esp_ringbuf/test_apps/main/test_ringbuf_common.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,3 +1042,84 @@ TEST_CASE("Test no-split buffers always receive items in order", "[esp_ringbuf][
10421042
// Cleanup
10431043
vRingbufferDelete(buffer_handle);
10441044
}
1045+
1046+
/* ---------------------------- Test no-split ring buffer SendAquire and SendComplete with dummy ---------------------------
1047+
* The following test case tests the SendAquire and SendComplete functions of the no-split ring buffer.
1048+
*
1049+
* The test case will do the following...
1050+
* 1) Create a no-split ring buffer.
1051+
* 2) Acquire space on the buffer to send an item.
1052+
* 3) Send the item to the buffer.
1053+
* 4) Verify that the item is received correctly.
1054+
* 5) Acquire space on the buffer until the buffer can no longer receive a full item without wrap around.
1055+
* 6) Send the items out-of-order to the buffer.
1056+
* 7) Verify that the items are not received until the first item is sent.
1057+
* 8) Send the first item.
1058+
* 9) Verify that the items are received in the correct order.
1059+
*/
1060+
TEST_CASE("Test no-split buffers always receive items in order (with dummy)", "[esp_ringbuf][linux]")
1061+
{
1062+
const uint8_t C_BUFFER_SIZE = 64;
1063+
const uint8_t C_ITEM_SIZE = 15;
1064+
1065+
// Create buffer
1066+
RingbufHandle_t buffer_handle = xRingbufferCreate(C_BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT);
1067+
TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
1068+
1069+
// Acquire space on the buffer to send an item and write to the item
1070+
void *item1;
1071+
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSendAcquire(buffer_handle, &item1, C_ITEM_SIZE, TIMEOUT_TICKS));
1072+
*(uint32_t *)item1 = 0x123;
1073+
1074+
// Send the item to the buffer
1075+
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSendComplete(buffer_handle, item1));
1076+
1077+
// Verify that the item is received correctly
1078+
size_t item_size;
1079+
uint32_t *received_item = xRingbufferReceive(buffer_handle, &item_size, TIMEOUT_TICKS);
1080+
TEST_ASSERT_NOT_NULL(received_item);
1081+
TEST_ASSERT_EQUAL(item_size, C_ITEM_SIZE);
1082+
TEST_ASSERT_EQUAL(*(uint32_t *)received_item, 0x123);
1083+
1084+
// Return the space to the buffer after receiving the item
1085+
vRingbufferReturnItem(buffer_handle, received_item);
1086+
1087+
// At this point, the buffer should be empty
1088+
UBaseType_t items_waiting;
1089+
vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting);
1090+
TEST_ASSERT_MESSAGE(items_waiting == 0, "Incorrect items waiting");
1091+
1092+
// Acquire space on the buffer until the buffer is full
1093+
#define MAX_NUM_ITEMS_DUMMY ( C_BUFFER_SIZE / ( C_ITEM_SIZE + ITEM_HDR_SIZE ) )
1094+
void *items[MAX_NUM_ITEMS_DUMMY];
1095+
for (int i = 0; i < MAX_NUM_ITEMS_DUMMY; i++) {
1096+
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSendAcquire(buffer_handle, &items[i], C_ITEM_SIZE, TIMEOUT_TICKS));
1097+
TEST_ASSERT_NOT_NULL(items[i]);
1098+
*(uint32_t *)items[i] = (0x100 + i);
1099+
}
1100+
1101+
// Verify that the buffer is full by attempting to acquire space for another item
1102+
void *another_item;
1103+
TEST_ASSERT_EQUAL(pdFALSE, xRingbufferSendAcquire(buffer_handle, &another_item, C_ITEM_SIZE, TIMEOUT_TICKS));
1104+
1105+
// Send the items out-of-order to the buffer. Verify that the items are not received until the first item is sent.
1106+
// In this case, we send the items in the reverse order until the first item is sent.
1107+
for (int i = MAX_NUM_ITEMS_DUMMY - 1; i > 0; i--) {
1108+
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSendComplete(buffer_handle, items[i]));
1109+
TEST_ASSERT_NULL(xRingbufferReceive(buffer_handle, &item_size, 0));
1110+
}
1111+
1112+
// Send the first item
1113+
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSendComplete(buffer_handle, items[0]));
1114+
1115+
// Verify that the items are received in the correct order
1116+
for (int i = 0; i < MAX_NUM_ITEMS_DUMMY; i++) {
1117+
received_item = xRingbufferReceive(buffer_handle, &item_size, TIMEOUT_TICKS);
1118+
TEST_ASSERT_NOT_NULL(received_item);
1119+
TEST_ASSERT_EQUAL(*(uint32_t *)received_item, (0x100 + i));
1120+
vRingbufferReturnItem(buffer_handle, received_item);
1121+
}
1122+
1123+
// Cleanup
1124+
vRingbufferDelete(buffer_handle);
1125+
}

0 commit comments

Comments
 (0)