Skip to content

Commit 50ac8aa

Browse files
committed
Fix rare silent connection hangs on Linux
The epoll backend silently ignored failures from epoll_ctl when registering, resubscribing, and unsubscribing event filters. An actor that thought it was subscribed would wait forever for a notification that never comes. Check epoll_ctl returns and send ASIO_ERROR to the owning actor on subscribe/resubscribe failure. Also fixes the init error check (was comparing against 0 instead of -1) and adds proper resource cleanup on partial init failure. Closes #5119 Closes #5126
1 parent 5b50d2c commit 50ac8aa

File tree

2 files changed

+29
-4
lines changed

2 files changed

+29
-4
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Fix rare silent connection hangs on Linux
2+
3+
On Linux, if the OS failed to register an I/O event (due to resource exhaustion or an FD race), the failure was silently ignored. Actors waiting for network events that were never registered would hang indefinitely with no error. This could appear as connections that never complete, listeners that stop accepting, or timers that stop firing — with no indication of what went wrong.
4+
5+
The runtime now detects these registration failures and notifies the affected actor, which tears down cleanly — the same as any other I/O failure. Stdlib consumers like `TCPConnection` and `TCPListener` handle this automatically.
6+
7+
Also fixes the ASIO backend init to correctly detect `epoll_create1` and `eventfd` failures (previously checked for 0 instead of -1), and to clean up all resources on partial init failure.

src/libponyrt/asio/epoll.c

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,13 @@ asio_backend_t* ponyint_asio_backend_init()
112112
b->epfd = epoll_create1(EPOLL_CLOEXEC);
113113
b->wakeup = eventfd(0, EFD_NONBLOCK);
114114

115-
if(b->epfd == 0 || b->wakeup == 0)
115+
if(b->epfd == -1 || b->wakeup == -1)
116116
{
117+
if(b->epfd != -1)
118+
close(b->epfd);
119+
if(b->wakeup != -1)
120+
close(b->wakeup);
121+
ponyint_messageq_destroy(&b->q, true);
117122
POOL_FREE(asio_backend_t, b);
118123
return NULL;
119124
}
@@ -122,7 +127,14 @@ asio_backend_t* ponyint_asio_backend_init()
122127
ep.data.ptr = b;
123128
ep.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
124129

125-
epoll_ctl(b->epfd, EPOLL_CTL_ADD, b->wakeup, &ep);
130+
if(epoll_ctl(b->epfd, EPOLL_CTL_ADD, b->wakeup, &ep) == -1)
131+
{
132+
close(b->epfd);
133+
close(b->wakeup);
134+
ponyint_messageq_destroy(&b->q, true);
135+
POOL_FREE(asio_backend_t, b);
136+
return NULL;
137+
}
126138

127139
#if !defined(USE_SCHEDULER_SCALING_PTHREADS)
128140
// Make sure we ignore signals related to scheduler sleeping/waking
@@ -186,7 +198,10 @@ PONY_API void pony_asio_event_resubscribe(asio_event_t* ev)
186198

187199
// only resubscribe if there is something to resubscribe to
188200
if (something_to_resub)
189-
epoll_ctl(b->epfd, EPOLL_CTL_MOD, ev->fd, &ep);
201+
{
202+
if(epoll_ctl(b->epfd, EPOLL_CTL_MOD, ev->fd, &ep) == -1)
203+
pony_asio_event_send(ev, ASIO_ERROR, 0);
204+
}
190205
}
191206

192207
// Kept to maintain backwards compatibility so folks don't
@@ -426,7 +441,8 @@ PONY_API void pony_asio_event_subscribe(asio_event_t* ev)
426441
ep.events |= EPOLLET;
427442
}
428443

429-
epoll_ctl(b->epfd, EPOLL_CTL_ADD, ev->fd, &ep);
444+
if(epoll_ctl(b->epfd, EPOLL_CTL_ADD, ev->fd, &ep) == -1)
445+
pony_asio_event_send(ev, ASIO_ERROR, 0);
430446
}
431447

432448
PONY_API void pony_asio_event_setnsec(asio_event_t* ev, uint64_t nsec)
@@ -459,6 +475,8 @@ PONY_API void pony_asio_event_unsubscribe(asio_event_t* ev)
459475
asio_backend_t* b = ponyint_asio_get_backend();
460476
pony_assert(b != NULL);
461477

478+
// Don't send ASIO_ERROR on delete failure — the actor is tearing down,
479+
// and ENOENT/EBADF is expected (FD already closed or never registered).
462480
epoll_ctl(b->epfd, EPOLL_CTL_DEL, ev->fd, NULL);
463481

464482
if(ev->flags & ASIO_TIMER)

0 commit comments

Comments
 (0)