fix: always remove socket from epoll in tcp_conn_handle_error
tcp_conn_handle_error гвардил uasync_remove_socket_t по tc->socket_id, пропуская повторный вызов. В одном epoll_wait batch два события (EPOLLERR + EPOLLOUT) на один fd: первое делает remove (socket_id=NULL), второе проходит guard и dispatch'ит write_cb на освобождённом tc. Фикс: всегда вызывать uasync_remove_socket_t + epoll_ctl(DEL), независимо от socket_id.
This commit is contained in:
1 parent
ccdbab95a8
commit
aff3b2d00f
1 file changed
+7
-4
+7
-4
@@ -32,7 +32,7 @@ struct tcp_conn* tcp_conn_create(
|
||||
void (*on_error)(struct tcp_conn* tc, int err, void* arg),
|
||||
void* arg)
|
||||
{
|
||||
if (!ua || sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_conn_create: invalid args ua=%p", ua); return NULL; }
|
||||
if (!ua || sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_conn_create: invalid args ua=%p", (void*)ua); return NULL; }
|
||||
|
||||
struct tcp_conn* tc = u_calloc(1, sizeof(struct tcp_conn));
|
||||
if (!tc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_conn_create: u_calloc failed"); return NULL; }
|
||||
@@ -157,11 +157,14 @@ static void tcp_conn_handle_error(struct tcp_conn* tc, int err)
|
||||
}
|
||||
}
|
||||
|
||||
if (tc->socket_id) {
|
||||
if (tc->sock != SOCKET_INVALID) {
|
||||
// Гарантированно удаляем из epoll (uasync_remove_socket_t может пропустить DEL если нода уже inactive)
|
||||
uasync_remove_socket_t(tc->ua, tc->sock);
|
||||
tc->socket_id = NULL;
|
||||
}
|
||||
if (tc->sock != SOCKET_INVALID) {
|
||||
#if HAS_EPOLL
|
||||
if (tc->ua && tc->ua->use_epoll && tc->ua->epoll_fd >= 0)
|
||||
epoll_ctl(tc->ua->epoll_fd, EPOLL_CTL_DEL, (int)tc->sock, NULL);
|
||||
#endif
|
||||
socket_close_wrapper(tc->sock);
|
||||
tc->sock = SOCKET_INVALID;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user