cancel
Showing results for 
Search instead for 
Did you mean: 

How to call LwIP thread-unsafe functions when using FreeRTOS?

_AdamNtrx
Associate III

Hello!

I am writing a program that uses FreeRTOS and LwIP libraries. I would want to check ARP table, but functions from ip4 directory are not considered thread-safe. How can I check ARP table when using FreeRTOS or how can I make sure calling thread-unsafe functions won't break my program?

I saw that there is an LwIP mutex that can be used to lock the stack via LOCK_TCPIP_CORE() and UNLOCK_TCPIP_CORE() macros, but I am not sure how to use them properly or if it is even a good approach.

 

Thank you in advance!

1 ACCEPTED SOLUTION

Accepted Solutions
_AdamNtrx
Associate III

I learned how to use tcp_callback to use thread-unsafe function in a safe way from multiple context. Here is an example.

 

Thread-safe version of etharp_find_addr function (etharp_find_addrTSW) that uses tcp_callback to pass it to tcpip_thread, callback version of etharp_find_addr that actually calls etharp_find_addr and structure used to store function's arguments, return value and semaphore

/* etharp_find_addr */
typedef struct {
	//arguments
	struct netif *netif;
	const ip4_addr_t *ipaddr;
	struct eth_addr **eth_ret;
	const ip4_addr_t **ip_ret;

    ssize_t etharpRet; //result
    osSemaphoreId_t sem; //to signal completion
} etharp_find_addrTSW_Struct;


static void etharp_find_addrTWS_Callback(void *argument) {
	etharp_find_addrTSW_Struct *fStr=(etharp_find_addrTSW_Struct *)argument;
    fStr->etharpRet=etharp_find_addr(fStr->netif, fStr->ipaddr, fStr->eth_ret,  fStr->ip_ret);
    osSemaphoreRelease(fStr->sem); // wake up the waiting task
}


ssize_t etharp_find_addrTSW(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr **eth_ret, const ip4_addr_t **ip_ret, uint32_t timeoutMs) {

	osStatus_t semReturn;
	ssize_t result;

	osSemaphoreAttr_t attr={
		.name="etharp_find_addrTSW"
	};

	etharp_find_addrTSW_Struct fStr={
		.netif=netif,
		.ipaddr=ipaddr,
		.eth_ret=eth_ret,
		.ip_ret=ip_ret,
		.sem=osSemaphoreNew(1, 0, &attr)
	};

    if (fStr.sem==NULL) {
        return -1; //out of resources
    }

    if (tcpip_callback(etharp_find_addrTWS_Callback, &fStr) != ERR_OK) {
        osSemaphoreDelete(fStr.sem);
        return -1;
    }

    //wait for the callback to run (or timeout)
    semReturn=osSemaphoreAcquire(fStr.sem, timeoutMs);
    osSemaphoreDelete(fStr.sem);
    if (semReturn != osOK) { // timed out before TCPIP thread could process
        return -1;
    }

    result=fStr.etharpRet;
    return result;
}

 

How to use:

ssize_t arpRet=etharp_find_addrTSW(NULL, &gnetif.gw, NULL, NULL, 5000);

 

 

I still don't know how to use LOCK_TCPIP_CORE() and UNLOCK_TCPIP_CORE() though.

View solution in original post

8 REPLIES 8
MOBEJ
ST Employee

Hello @_AdamNtrx ?

 

Could you please confirm which STM32 board you are using  for your project? This will help us provide you with the accurate example of LWIP using FreeRTOS.

 

Br

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
TDK
Super User

Use a mutex to ensure only one thread is calling a function at once. Or, only call LwIP functions from a single thread.

If you feel a post has answered your question, please click "Accept as Solution".

I use STM32F769I-DISCOVERY board.

Does that mean as long as unsafe LwIP functions are not called simultaneously they are fine to use?

Yes and no.

As long as you keep your LwIP calls to one thread / task, you are safe.
In a preemptive multitasking system, any calls to the same function from different tasks will eventually be "simultaneous", statistically speaking.

 

Alright, so if LwIP functions are either called only in one task or are protected by mutex, they are fine. Got it.

 

What about (UN)LOCK_TCPIP_CORE() macros? How to use them? Or should they be used at all?

Hello @_AdamNtrx , 

Please check out the lwIP projects on GitHub for the STM32F769I board. It can help you with your issue

 

BR

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
_AdamNtrx
Associate III

I learned how to use tcp_callback to use thread-unsafe function in a safe way from multiple context. Here is an example.

 

Thread-safe version of etharp_find_addr function (etharp_find_addrTSW) that uses tcp_callback to pass it to tcpip_thread, callback version of etharp_find_addr that actually calls etharp_find_addr and structure used to store function's arguments, return value and semaphore

/* etharp_find_addr */
typedef struct {
	//arguments
	struct netif *netif;
	const ip4_addr_t *ipaddr;
	struct eth_addr **eth_ret;
	const ip4_addr_t **ip_ret;

    ssize_t etharpRet; //result
    osSemaphoreId_t sem; //to signal completion
} etharp_find_addrTSW_Struct;


static void etharp_find_addrTWS_Callback(void *argument) {
	etharp_find_addrTSW_Struct *fStr=(etharp_find_addrTSW_Struct *)argument;
    fStr->etharpRet=etharp_find_addr(fStr->netif, fStr->ipaddr, fStr->eth_ret,  fStr->ip_ret);
    osSemaphoreRelease(fStr->sem); // wake up the waiting task
}


ssize_t etharp_find_addrTSW(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr **eth_ret, const ip4_addr_t **ip_ret, uint32_t timeoutMs) {

	osStatus_t semReturn;
	ssize_t result;

	osSemaphoreAttr_t attr={
		.name="etharp_find_addrTSW"
	};

	etharp_find_addrTSW_Struct fStr={
		.netif=netif,
		.ipaddr=ipaddr,
		.eth_ret=eth_ret,
		.ip_ret=ip_ret,
		.sem=osSemaphoreNew(1, 0, &attr)
	};

    if (fStr.sem==NULL) {
        return -1; //out of resources
    }

    if (tcpip_callback(etharp_find_addrTWS_Callback, &fStr) != ERR_OK) {
        osSemaphoreDelete(fStr.sem);
        return -1;
    }

    //wait for the callback to run (or timeout)
    semReturn=osSemaphoreAcquire(fStr.sem, timeoutMs);
    osSemaphoreDelete(fStr.sem);
    if (semReturn != osOK) { // timed out before TCPIP thread could process
        return -1;
    }

    result=fStr.etharpRet;
    return result;
}

 

How to use:

ssize_t arpRet=etharp_find_addrTSW(NULL, &gnetif.gw, NULL, NULL, 5000);

 

 

I still don't know how to use LOCK_TCPIP_CORE() and UNLOCK_TCPIP_CORE() though.