2025-03-24 3:07 AM
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!
Solved! Go to Solution.
2025-07-23 11:52 PM
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.
2025-03-24 5:47 AM
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
2025-03-24 5:48 AM
Use a mutex to ensure only one thread is calling a function at once. Or, only call LwIP functions from a single thread.
2025-03-24 11:20 PM
I use STM32F769I-DISCOVERY board.
2025-03-24 11:21 PM
Does that mean as long as unsafe LwIP functions are not called simultaneously they are fine to use?
2025-03-25 12:13 AM
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.
2025-03-25 12:49 AM
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?
2025-03-26 6:04 AM
Hello @_AdamNtrx ,
Please check out the lwIP projects on GitHub for the STM32F769I board. It can help you with your issue
BR
2025-07-23 11:52 PM
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.