From 898cac0061b743230418fae8aaf547e6dc742ca5 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Wed, 20 Jan 2021 09:19:49 +0100 Subject: [PATCH] modbus: Exit server task gracefully to correctly cleanup lwip internals Current lwip implementation does not support deleting a task which is actively waiting on `select()` or `poll()` API. Therefore we have to make sure that `select()` exits to deallocate its internal callback before deleting the task. This is achieved by a shutdown semaphore which informs the client once the `select()` exitted. fix slave --- .../tcp_slave/port/port_tcp_slave.c | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/components/freemodbus/tcp_slave/port/port_tcp_slave.c b/components/freemodbus/tcp_slave/port/port_tcp_slave.c index 67f04c2351..01ff9b026c 100644 --- a/components/freemodbus/tcp_slave/port/port_tcp_slave.c +++ b/components/freemodbus/tcp_slave/port/port_tcp_slave.c @@ -66,6 +66,7 @@ void vMBPortEventClose( void ); /* ----------------------- Static variables ---------------------------------*/ static int xListenSock = -1; +static SemaphoreHandle_t xShutdownSemaphore = NULL; static MbSlavePortConfig_t xConfig = { 0 }; /* ----------------------- Static functions ---------------------------------*/ @@ -462,6 +463,11 @@ static void vMBTCPPortServerTask(void *pvParameters) // Wait for an activity on one of the sockets, timeout is NULL, so wait indefinitely xErr = select(xMaxSd + 1 , &xReadSet , NULL , NULL , NULL); if ((xErr < 0) && (errno != EINTR)) { + // First check if the task is not flagged for shutdown + if (xListenSock == -1 && xShutdownSemaphore) { + xSemaphoreGive(xShutdownSemaphore); + vTaskDelete(NULL); + } // error occurred during wait for read ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "select() errno = %d.", errno); continue; @@ -626,8 +632,22 @@ void vMBTCPPortClose( ) { // Release resources for the event queue. + + // Try to exit the task gracefully, so select could release its internal callbacks + // that were allocated on the stack of the task we're going to delete + xShutdownSemaphore = xSemaphoreCreateBinary(); + vTaskResume(xConfig.xMbTcpTaskHandle); + if (xShutdownSemaphore == NULL || // if no semaphore (alloc issues) or couldn't acquire it, just delete the task + xSemaphoreTake(xShutdownSemaphore, 2*pdMS_TO_TICKS(CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND)) != pdTRUE) { + ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Task couldn't exit gracefully within timeout -> abruptly deleting the task"); + vTaskDelete(xConfig.xMbTcpTaskHandle); + } + if (xShutdownSemaphore) { + vSemaphoreDelete(xShutdownSemaphore); + xShutdownSemaphore = NULL; + } + vMBPortEventClose( ); - vTaskDelete(xConfig.xMbTcpTaskHandle); } void