I'm running LwIP 1.4.1 and have some questions about the event_callback() in sockets.c
In my project, I am experiencing a crash related to synchronization in event_callback() and an application thread calling select(). My project is a uniprocessor system running an RTOS that implements a static priority scheduler. SYS_ARCH_PROTECT() is implemented by disabling interrupts. sys_sem_signal() is implemented using a counting semaphore. TCPIP thread is higher priority than application threads. The crash happens when the application thread is waiting in select() and the TCPIP thread is calling event_callback() to process an event. What's happen is in the below loop, calling sys_sem_signal() results in a context switch on my project's RTOS even though application thread is lower priority. The RTOS's semaphore construct doesn't support priority inheritance/elevation. The application thread wakes up and finishes the select call, modifying the select_cb_list. When the context switches back to TCPIP thread, it finishes the loop iteration and crashes because the select_cb_list has been modified. What I've done to mitigate the context switch is move the line last_select_cb_ctr = select_cb_ctr; to the top of the for loop. To me the loop already had handling for a context switch per iteration, but it only saved the counter at the end. So now it can handle a switch in the call to sys_sem_signal() as well. My questions to whether this is a bug depend on: 1) Should SYS_ARCH_PROTECT() do more than just disable interrupts? Something that would act as a critical section in the case where a context switch happens? 2) Is it assumed that calling sys_sem_signal() will not cause a voluntary context switch? SYS_ARCH_PROTECT(lev); ... again: for (scb = select_cb_list; scb != NULL; scb = scb->next) { if (scb->sem_signalled == 0) { /* semaphore not signalled yet */ int do_signal = 0; /* Test this select call for our socket */ if (sock->rcvevent > 0) { if (scb->readset && FD_ISSET(s, scb->readset)) { do_signal = 1; } } if (sock->sendevent != 0) { if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) { do_signal = 1; } } if (sock->errevent != 0) { if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) { do_signal = 1; } } if (do_signal) { scb->sem_signalled = 1; /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might lead to the select thread taking itself off the list, invalidagin the semaphore. */ sys_sem_signal(&scb->sem); } } last_select_cb_ctr = select_cb_ctr; /* unlock interrupts with each step */ SYS_ARCH_UNPROTECT(lev); /* this makes sure interrupt protection time is short */ SYS_ARCH_PROTECT(lev); if (last_select_cb_ctr != select_cb_ctr) { /* someone has changed select_cb_list, restart at the beginning */ goto again; } Thanks, Joel
_______________________________________________ lwip-users mailing list lwip-users@nongnu.org https://lists.nongnu.org/mailman/listinfo/lwip-users