Выглядит race следующим образом (6.3-SMP):
48976 nginx 1202926950.760500 CALL recvfrom(0xad,0x1b2ab38,0x400,0,0,0)
48976 nginx 1202926950.760511 GIO fd 173 read 122 bytes
"HTTP/1.0 200 OK\r
Client-Host: [UNAVAILABLE]\r
Auth-Status: OK\r
Auth-Server: 10.8.2.19\r
Auth-Port: 25\r
Connection: close\r
\r
"
48976 nginx 1202926950.760521 RET recvfrom 122/0x7a
48976 nginx 1202926950.760533 CALL close(0xad)
48976 nginx 1202926950.760548 RET close -1 errno 57 Socket is not
connected
Похоже, race возникает тут:
sys/kern/uipc_socket.c:
int soclose(so)
{
...
if (so->so_state & SS_ISCONNECTED) {
if ((so->so_state & SS_ISDISCONNECTING) == 0) {
error = sodisconnect(so);
}
int sodisconnect(so)
{
...
if ((so->so_state & SS_ISCONNECTED) == 0)
return (ENOTCONN);
}
so_state для unix сокета устанавливается следующим образом:
sys/kern/uipc_usrreq.c:
static void
unp_disconnect(struct unpcb *unp)
{
struct unpcb *unp2 = unp->unp_conn;
struct socket *so;
UNP_LOCK_ASSERT();
if (unp2 == NULL)
return;
unp->unp_conn = NULL;
switch (unp->unp_socket->so_type) {
case SOCK_DGRAM:
LIST_REMOVE(unp, unp_reflink);
so = unp->unp_socket;
SOCK_LOCK(so);
so->so_state &= ~SS_ISCONNECTED;
SOCK_UNLOCK(so);
break;
case SOCK_STREAM:
soisdisconnected(unp->unp_socket);
unp2->unp_conn = NULL;
soisdisconnected(unp2->unp_socket);
break;
}
}
Насколько я понял, для sock_stream закрываются оба конца соединения.
Закрываются так:
sys/kern/uipc_socket2.c:
void
soisdisconnected(so)
register struct socket *so;
{
/*
* XXXRW: This code assumes that SOCK_LOCK(so) and
* SOCKBUF_LOCK(&so->so_rcv) are the same.
*/
SOCKBUF_LOCK(&so->so_rcv);
so->so_state &=
~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING);
so->so_state |= SS_ISDISCONNECTED;
so->so_rcv.sb_state |= SBS_CANTRCVMORE;
sorwakeup_locked(so);
SOCKBUF_LOCK(&so->so_snd);
so->so_snd.sb_state |= SBS_CANTSENDMORE;
sbdrop_locked(&so->so_snd, so->so_snd.sb_cc);
sowwakeup_locked(so);
wakeup(&so->so_timeo);
}
То есть, не исключена ситуация, когда в функции soclose у одного из
сокетов состояние SS_ISCONNECTED, а в функции sodisconnect этот флаг уже
успевает убраться. Тогда может вернуться ENOTCONN. Как вариант фикса можно попробовать убрать проверку
if ((so->so_state & SS_ISCONNECTED) == 0)
return (ENOTCONN);
из функции sodisconnect, например, возвращая в этом месте 0.
No comments:
Post a Comment