diff --git a/components/esp_ringbuf/include/freertos/ringbuf.h b/components/esp_ringbuf/include/freertos/ringbuf.h index 6791975072..02d252afbf 100644 --- a/components/esp_ringbuf/include/freertos/ringbuf.h +++ b/components/esp_ringbuf/include/freertos/ringbuf.h @@ -386,6 +386,16 @@ void vRingbufferReturnItem(RingbufHandle_t xRingbuffer, void *pvItem); */ void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, BaseType_t *pxHigherPriorityTaskWoken); +/** + * @brief Reset a ring buffer back to its original empty state + * + * @param[in] xRingbuffer Ring buffer to reset + * + * @return ESP_ERR_INVALID_STATE if one or more items are not sent, completed or returned + * ESP_OK if the operation was successful + */ +esp_err_t vRingbufferReset(RingbufHandle_t xRingbuffer); + /** * @brief Delete a ring buffer * diff --git a/components/esp_ringbuf/ringbuf.c b/components/esp_ringbuf/ringbuf.c index f205f9afad..7addde73a4 100644 --- a/components/esp_ringbuf/ringbuf.c +++ b/components/esp_ringbuf/ringbuf.c @@ -1252,6 +1252,38 @@ void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, Bas portEXIT_CRITICAL_ISR(&pxRingbuffer->mux); } +esp_err_t vRingbufferReset(RingbufHandle_t xRingbuffer) +{ + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + + portENTER_CRITICAL(&pxRingbuffer->mux); + // Check for unreturned buffers + if ((pxRingbuffer->pucAcquire != pxRingbuffer->pucWrite) || + (pxRingbuffer->pucRead != pxRingbuffer->pucFree)) { + portEXIT_CRITICAL(&pxRingbuffer->mux); + return ESP_ERR_INVALID_STATE; + } + + // Reset state + pxRingbuffer->pucAcquire = pxRingbuffer->pucHead; + pxRingbuffer->pucWrite = pxRingbuffer->pucHead; + pxRingbuffer->pucRead = pxRingbuffer->pucHead; + pxRingbuffer->pucFree = pxRingbuffer->pucHead; + pxRingbuffer->xItemsWaiting = 0; + pxRingbuffer->uxRingbufferFlags &= ~rbBUFFER_FULL_FLAG; + + // If there's a task waiting to transmit, unblock it + if (listLIST_IS_EMPTY(&pxRingbuffer->xTasksWaitingToSend) == pdFALSE) { + if (xTaskRemoveFromEventList(&pxRingbuffer->xTasksWaitingToSend) == pdTRUE) { + portYIELD_WITHIN_API(); + } + } + portEXIT_CRITICAL(&pxRingbuffer->mux); + + return ESP_OK; +} + void vRingbufferDelete(RingbufHandle_t xRingbuffer) { Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; diff --git a/components/esp_ringbuf/test_apps/main/test_ringbuf_common.c b/components/esp_ringbuf/test_apps/main/test_ringbuf_common.c index 028671dbe0..c5e81d7a4c 100644 --- a/components/esp_ringbuf/test_apps/main/test_ringbuf_common.c +++ b/components/esp_ringbuf/test_apps/main/test_ringbuf_common.c @@ -20,6 +20,7 @@ #include "freertos/ringbuf.h" #include "unity.h" #include "esp_rom_sys.h" +#include "esp_task.h" #include "test_functions.h" @@ -1318,3 +1319,61 @@ TEST_CASE("Test no-split full buffer becomes empty when oldest is returned last" TEST_ASSERT_NOT_NULL(slot); vRingbufferDelete(buffer_handle); } + +/* ----------------------------------- Test ring buffer reset -------------------------------------- + * Reset Case: Test that vRingbufferReset() empties the ring buffer + * Reset unblocks sender: Test that vRingbufferReset() will unblock a previously blocked sender + */ + +static uint8_t item[SMALL_ITEM_SIZE] = {0}; + +TEST_CASE("Test ring buffer reset", "[esp_ringbuf][linux]") +{ + // Create buffer + RingbufHandle_t rb = xRingbufferCreate(SMALL_ITEM_SIZE, RINGBUF_TYPE_BYTEBUF); + TEST_ASSERT_MESSAGE(rb != NULL, "Failed to create ring buffer"); + // Fill buffer + TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSend(rb, item, SMALL_ITEM_SIZE, 0)); + // Confirm buffer is full + TEST_ASSERT_EQUAL(0, xRingbufferGetCurFreeSize(rb)); + // Reset ring buffer + vRingbufferReset(rb); + // Confirm buffer is empty + TEST_ASSERT_EQUAL(SMALL_ITEM_SIZE, xRingbufferGetCurFreeSize(rb)); + // Cleanup + vRingbufferDelete(rb); +} + +static volatile bool post_reset_send = false; + +static void post_reset_send_task(void *arg) +{ + RingbufHandle_t rb = (RingbufHandle_t)arg; + TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSend(rb, item, SMALL_ITEM_SIZE, portMAX_DELAY)); + post_reset_send = true; + vTaskDelete(NULL); +} + +TEST_CASE("Test ring buffer reset unblocks sender ", "[esp_ringbuf][linux]") +{ + // Create buffer + RingbufHandle_t rb = xRingbufferCreate(SMALL_ITEM_SIZE, RINGBUF_TYPE_BYTEBUF); + TEST_ASSERT_MESSAGE(rb != NULL, "Failed to create ring buffer"); + // Fill buffer + TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSend(rb, item, SMALL_ITEM_SIZE, 0)); + // Confirm buffer is full + TEST_ASSERT_EQUAL(0, xRingbufferGetCurFreeSize(rb)); + // Launch task to block on sending to full ring buffer + post_reset_send = false; + xTaskCreatePinnedToCore(post_reset_send_task, "send tsk", 2048, (void*)rb, ESP_TASK_MAIN_PRIO + 1, NULL, 0); + vTaskDelay(10); + // Confirm blocked task has not sent + TEST_ASSERT_EQUAL(false, post_reset_send); + // Reset the ring buffer + vRingbufferReset(rb); + + TEST_ASSERT_EQUAL(true, post_reset_send); + // Cleanup + vRingbufferDelete(rb); + vTaskDelay(1); +}