NUCLEO-H743ZI2: TCP server hangs up when RTOS create task to handle the connection

Hello,
I need to make a simple TCP server with up to 5 clients connection on the base of the NUCLEO-H743ZI2 board. I use the STM32_Nucleo_H723ZG_FreeRTOS_TCP project as starting point. I was able to compile it and run in STM32Cube IDE. I added the blinking LED to this code and replaced vStartTCPEchoClientTasks_SingleTasks by my vStartTCPServerTask_SingleTask which is defined in tcp_server.c and looks next:

#include "FreeRTOS.h"
#include "FreeRTOS_IP.h"

#define MAX_NUM_CLIENTS		5
#define TCP_SERVER_PORT		5000

/* Stores the stack size passed into vStartSimpleTCPServerTasks() so it can be
reused when the server listening task creates tasks to handle connections. */
static uint16_t usUsedStackSize = 0;
/*
 * Uses FreeRTOS+TCP to listen for incoming TCP connections, creating a task
 * to handle each connection.
 */
void prvTcpServerTask( void *pvParameters );
/*
 * Created by the connection listening task to handle a single connection.
 */
void prvServerConnectionInstance( void *pvParameters );
/*-----------------------------------------------------------*/
static uint32_t ulConnectionCount = 0; // Number of clients connected to server
/*-----------------------------------------------------------*/
void vStartTCPServerTask_SingleTask( uint16_t usTaskStackSize, UBaseType_t uxTaskPriority )
{
	xTaskCreate( 	prvTcpServerTask,	/* The function that implements the task. */
					"TcpSrv",			/* Just a text name for the task to aid debugging. */
					usTaskStackSize,	/* The stack size is defined in FreeRTOSIPConfig.h. */
					NULL,				/* The task parameter, not used in this case. */
					uxTaskPriority,		/* The priority assigned to the task is defined in FreeRTOSConfig.h. */
					NULL );				/* The task handle is not used. */

	usUsedStackSize = usTaskStackSize;
}
/*-----------------------------------------------------------*/
void prvTcpServerTask( void *pvParameters )
{
	struct freertos_sockaddr xClient, xBindAddress;
	Socket_t xListeningSocket, xConnectedSocket;
	socklen_t xSize = sizeof( xClient );
	static const TickType_t xRxTimeOut = portMAX_DELAY;
	static const TickType_t xTxTimeOut = portMAX_DELAY;
	const BaseType_t xBacklog = 10;

    /* Attempt to open the socket. */
    xListeningSocket = FreeRTOS_socket( FREERTOS_AF_INET4, /* Or FREERTOS_AF_INET6 for IPv6. */
    									FREERTOS_SOCK_STREAM,  /* SOCK_STREAM for TCP. */
										FREERTOS_IPPROTO_TCP );

    /* Check the socket was created. */
    configASSERT( xListeningSocket != FREERTOS_INVALID_SOCKET );

    /* If FREERTOS_SO_RCVBUF or FREERTOS_SO_SNDBUF are to be used with
       FreeRTOS_setsockopt() to change the buffer sizes from their default then do
       it here!. (see the FreeRTOS_setsockopt() documentation. */

    /* If ipconfigUSE_TCP_WIN is set to 1 and FREERTOS_SO_WIN_PROPERTIES is to
       be used with FreeRTOS_setsockopt() to change the sliding window size from
       its default then do it here! (see the FreeRTOS_setsockopt()
       documentation. */

    /* Set a time out so accept() will just wait for a connection. */
    /* Set send and receive time outs. */
    FreeRTOS_setsockopt( xListeningSocket,
                         0,
                         FREERTOS_SO_RCVTIMEO,
                         &xRxTimeOut,
                         sizeof( xRxTimeOut ) );

	/* Bind the socket to the port that the client task will send to, then
	listen for incoming connections. */
    memset( &xBindAddress, 0, sizeof(xBindAddress) );
    xBindAddress.sin_port = FreeRTOS_htons(TCP_SERVER_PORT);
    xBindAddress.sin_family = FREERTOS_AF_INET4; /* FREERTOS_AF_INET6 to be used for IPv6 */

    /* Bind the socket to the port that the client RTOS task will send to. */
    FreeRTOS_bind( xListeningSocket, &xBindAddress, sizeof( xBindAddress ) );

    /* Set the socket into a listening state so it can accept connections.
       The maximum number of simultaneous connections is limited to 20. */
    FreeRTOS_listen( xListeningSocket, xBacklog );

    for( ;; )
    {
        /* Wait for incoming connections. */
        xConnectedSocket = FreeRTOS_accept( xListeningSocket, &xClient, &xSize );
        configASSERT( xConnectedSocket != FREERTOS_INVALID_SOCKET );

        BaseType_t xReturned;
        TaskHandle_t xHandle = NULL;

        /* Spawn a RTOS task to handle the connection. */
        xReturned = xTaskCreate( prvServerConnectionInstance,
                     "TCPServer",
                     usUsedStackSize,
                     ( void * ) xConnectedSocket,
                     tskIDLE_PRIORITY,
					 &xHandle );


        configASSERT( xReturned != pdPASS );
        if( xReturned == pdPASS )
        {
            /* The task was created. Use the task's handle to delete the task. */
//            configPRINTF( ( "xTaskCreate Error \n" ) );
            vTaskDelete( xHandle );
        }
    }
}
/*-----------------------------------------------------------*/
void prvServerConnectionInstance( void *pvParameters )
{
...
}

I just removed some code which not important.
And it is worked - I was able to connect by 2 clients and even send receive some data from them.
But at some point, my microcontroller started to hang up when the server trying to create a task to handle the connection. And I do not understand why this happened. My code was worked before and as I remember I did not change configuration.
So any helpful idea or recommendation is welcome!

Can you break the code in the debugger and see what it is doing when it appears stuck?

I tried. The xReturned = xTaskCreate () returns 1 (==pdPass) - I see this in Variables window. But everything hangs up on next line: configASSERT( xReturned != pdPASS );
And hangs up do not depend on content of this next line. Something wrong with xTaskCreate in my case.
Please have a look screenshot.

This assert failure indicates that xReturned is pdFAIL which is not the case looking at the variables window. Can you please show the register values also? Also, if you remove this assert and place a breakpoint on vTaskDelete call in the next if block, do you hit that?

why do you delete the worker task right after you create it without waiting for it to finish what it is supposed to do? Smells like trouble…

Thank you for pointing @RAc. I just realized that the assert is also incorrect in their code. Please change the assert to configASSERT( xReturned == pdPASS ); and remove the next if block which deletes the task right after creation.

Thank you, @aggarg and @RAc!
After correcting to if( xReturned != pdPASS ) now my system works again. This is strange - before I tried to comment these lines and microcontroller hang up too.
But today is a good day - the problem came out of nowhere and disappeared on its own.
Thank you again!

Here is some output in terminal window:

prvAcceptWaitClient: client 2401b1a0 parent 240196c0
Client = 1 sent to server 6 bytes
prvAcceptWaitClient: client 2401b438 parent 240196c0
Heap: current 164056 lowest 162496
Client = 2 sent to server 6 bytes

well, if you still delete the task while it is doing something, your system just works coincidentally - or appears to. If your worker task has a higher prioritymthan your listener task AND its computation is CPU bound, you are lucky that you do not hit the delete in the listener task too early, but in that scenario, your listener task will not have a chance to dispatch a new client task concurrently, so all benefits of your multitasking will be gone and you would be better off serving the clients in turn.

Thank you for recommendations, @RAc!
Could you please clarify one thing. Let imagine I have a 5 clients connected to my server. For each of them I should create a separate task as I understood.
And mainly each task will just wait some income message from client and react to them.
But what should I do if one of the clients will decide to disconnect? Where I should properly close the corresponding Task?
Thank you in advance for explanations!

There are several examples for multi-client servers available, I am sure others will poijt you directly to them (I currenrly do not have the bandwidth to look for them myself, apologies). In a nutshell, the protocol will dictate when a client communication is done, and the corresponding task must then delete itself using xTaskDelete(NULL).

Here is an example - FreeRTOS/FreeRTOS-Plus/Demo/FreeRTOS_Plus_TCP_Echo_Posix/SimpleTCPEchoServer.c at main · FreeRTOS/FreeRTOS · GitHub. The connection handler task deletes itself here.

I’d like to add that it’s a design decision to create separate client handler tasks or not.
It’s also absolutely possible to omit the somewhat costly separate tasks and for example handle multiple client connections in just 1 networking task, forward decoded requests to a central processing task and send back the corresponding replies via networking task to each client. Just for completeness.

1 Like