8000 Introduce a timeout to prevent `rb_thread_fd_select` from hanging wit… · github/ruby@3be1baa · GitHub
[go: up one dir, main page]

Skip to content {"props":{"docsUrl":"https://docs.github.com/get-started/accessibility/keyboard-shortcuts"}}

Commit 3be1baa

Browse files
authored
Introduce a timeout to prevent rb_thread_fd_select from hanging with write(2) failure (ruby#12457)
Rarely, there are cases where a write(2) call from a child thread to notify the main thread of the completion of name resolution fails. If this happens while the main thread is waiting in `rb_thread_fd_select`, rb_thread_fd_select may not notice that the name resolution has completed and end up hanging. This issue becomes a problem when there are no sockets currently being connected, no addresses ready for immediate connection attempts, and name resolution has already completed for one address family while the main thread is waiting for the name resolution of the other address family. (If name resolution is not completed for either address family, the chances of write(2) failing in both child threads are likely low.) To avoid this issue, a timeout is introduced to rb_thread_fd_select under the above conditions. This way, even if the issue occurs, the completion of name resolution should still be detected in the subsequent `if (!resolution_store.is_all_finished) ...` block.
1 parent 07e89bd commit 3be1baa

File tree

1 file changed

+13
-1
lines changed

1 file changed

+13
-1
lines changed

ext/socket/ipsocket.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,18 @@ init_fast_fallback_inetsock_internal(VALUE v)
854854
delay = tv_to_timeout(ends_at, now);
855855
delay_p = &delay;
856856
} else {
857-
delay_p = NULL;
857+
if (((resolution_store.v6.finished && !resolution_store.v4.finished) ||
858+
(resolution_store.v4.finished && !resolution_store.v6.finished)) &&
859+
!any_addrinfos(&resolution_store) &&
860+
!in_progress_fds(arg->connection_attempt_fds_size)) {
861+
/* A limited timeout is introduced to prevent select(2) from hanging when it is exclusively
862+
* waiting for name resolution and write(2) failure occurs in a child thread. */
863+
delay.tv_sec = 0;
864+
delay.tv_usec = 50000;
865+
delay_p = &delay;
866+
} else {
867+
delay_p = NULL;
868+
}
858869
}
859870

860871
nfds = 0;
@@ -1040,6 +1051,7 @@ init_fast_fallback_inetsock_internal(VALUE v)
10401051
status = 0;
10411052
}
10421053

1054+
/* For cases where write(2) fails in child threads */
10431055
if (!resolution_store.is_all_finised) {
10441056
if (!resolution_store.v6.finished && arg->getaddrinfo_entries[IPV6_ENTRY_POS]->has_syserr) {
10451057
resolution_store.v6.finished = true;

0 commit comments

Comments
 (0)
0