Fixed bug #31741: lwip_select seems to have threading problems

This commit is contained in:
goldsimon 2011-01-24 19:28:28 +00:00
parent effcb90fdf
commit 03be8f88fe
2 changed files with 43 additions and 42 deletions

View File

@ -233,6 +233,9 @@ HISTORY
++ Bugfixes: ++ Bugfixes:
2011-01-24: Simon Goldschmidt
* sockets.c: Fixed bug #31741: lwip_select seems to have threading problems
2010-12-02: Simon Goldschmidt 2010-12-02: Simon Goldschmidt
* err.h: Fixed ERR_IS_FATAL so that ERR_WOULDBLOCK is not fatal. * err.h: Fixed ERR_IS_FATAL so that ERR_WOULDBLOCK is not fatal.

View File

@ -1188,6 +1188,8 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL); LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
select_cb.prev->next = select_cb.next; select_cb.prev->next = select_cb.next;
} }
/* Increasing this counter tells even_callback that the list has changed. */
select_cb_ctr++;
SYS_ARCH_UNPROTECT(lev); SYS_ARCH_UNPROTECT(lev);
sys_sem_free(&select_cb.sem); sys_sem_free(&select_cb.sem);
@ -1230,6 +1232,7 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
int s; int s;
struct lwip_sock *sock; struct lwip_sock *sock;
struct lwip_select_cb *scb; struct lwip_select_cb *scb;
int last_select_cb_ctr;
SYS_ARCH_DECL_PROTECT(lev); SYS_ARCH_DECL_PROTECT(lev);
LWIP_UNUSED_ARG(len); LWIP_UNUSED_ARG(len);
@ -1292,56 +1295,51 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
return; return;
} }
SYS_ARCH_UNPROTECT(lev);
/* Now decide if anyone is waiting for this socket */ /* Now decide if anyone is waiting for this socket */
/* NOTE: This code is written this way to protect the select link list /* NOTE: This code goes through the select_cb_list list multiple times
but to avoid a deadlock situation by releasing select_lock before ONLY IF a select was actually waiting. We go through the list the number
signalling for the select. This means we need to go through the list of waiting select calls + 1. This list is expected to be small. */
multiple times ONLY IF a select was actually waiting. We go through
the list the number of waiting select calls + 1. This list is /* At this point, SYS_ARCH is still protected! */
expected to be small. */ again:
while (1) { for (scb = select_cb_list; scb != NULL; scb = scb->next) {
int last_select_cb_ctr; if (scb->sem_signalled == 0) {
SYS_ARCH_PROTECT(lev); /* semaphore not signalled yet */
for (scb = select_cb_list; scb; scb = scb->next) { int do_signal = 0;
/* @todo: unprotect with each loop and check for changes? */ /* Test this select call for our socket */
if (scb->sem_signalled == 0) { if (sock->rcvevent > 0) {
/* Test this select call for our socket */
if (scb->readset && FD_ISSET(s, scb->readset)) { if (scb->readset && FD_ISSET(s, scb->readset)) {
if (sock->rcvevent > 0) { do_signal = 1;
break;
}
}
if (scb->writeset && FD_ISSET(s, scb->writeset)) {
if (sock->sendevent != 0) {
break;
}
}
if (scb->exceptset && FD_ISSET(s, scb->exceptset)) {
if (sock->errevent != 0) {
break;
}
} }
} }
/* unlock interrupts with each step */ if (sock->sendevent != 0) {
last_select_cb_ctr = select_cb_ctr; if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
SYS_ARCH_UNPROTECT(lev); do_signal = 1;
SYS_ARCH_PROTECT(lev); }
if (last_select_cb_ctr != select_cb_ctr) { }
/* someone has changed select_cb_list, restart at the beginning */ if (sock->errevent != 0) {
scb = select_cb_list; 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);
} }
} }
if (scb) { /* unlock interrupts with each step */
scb->sem_signalled = 1; last_select_cb_ctr = select_cb_ctr;
sys_sem_signal(&scb->sem); SYS_ARCH_UNPROTECT(lev);
SYS_ARCH_UNPROTECT(lev); /* this makes sure interrupt protection time is short */
} else { SYS_ARCH_PROTECT(lev);
SYS_ARCH_UNPROTECT(lev); if (last_select_cb_ctr != select_cb_ctr) {
break; /* someone has changed select_cb_list, restart at the beginning */
goto again;
} }
} }
SYS_ARCH_UNPROTECT(lev);
} }
/** /**