diff --git a/components/esp_ringbuf/include/freertos/ringbuf.h b/components/esp_ringbuf/include/freertos/ringbuf.h index 6791975072..ba9f0696dd 100644 --- a/components/esp_ringbuf/include/freertos/ringbuf.h +++ b/components/esp_ringbuf/include/freertos/ringbuf.h @@ -386,6 +386,15 @@ 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 + * + * @note Users must ensure that all buffers are returned before calling this function + */ +void 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..85f4142f1e 100644 --- a/components/esp_ringbuf/ringbuf.c +++ b/components/esp_ringbuf/ringbuf.c @@ -1252,6 +1252,32 @@ void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, Bas portEXIT_CRITICAL_ISR(&pxRingbuffer->mux); } +void vRingbufferReset(RingbufHandle_t xRingbuffer) +{ + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + + portENTER_CRITICAL(&pxRingbuffer->mux); + // Check for unreturned buffers + configASSERT(pxRingbuffer->pucAcquire == pxRingbuffer->pucWrite); + configASSERT(pxRingbuffer->pucRead == pxRingbuffer->pucFree); + // 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); +} + 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..64418c1d58 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,62 @@ 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); + // Check task has unblocked and sent + vTaskDelay(1); + TEST_ASSERT_EQUAL(true, post_reset_send); + // Cleanup + vRingbufferDelete(rb); + vTaskDelay(1); +}