Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Make waiting_fd behaviour per-IO. #13127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 13, 2025
Merged

Conversation

ioquatix
Copy link
Member

See #5384 for full context.

@ioquatix ioquatix requested review from ko1, akr, nobu and Copilot April 18, 2025 01:06
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the “waiting_fd” behavior so that waiting and blocking operations are managed per‑IO rather than via a global list. Key changes include:

  • Removing the global waiting_fd list initialization and associated memory‐size functions (e.g. in vm_core.h and vm.c).
  • Replacing the waiting_fd structure and its helper functions in thread.c with per‑IO blocking operation nodes.
  • Eliminating the thread_fd close test file, consistent with the new per‑IO design.

Reviewed Changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
vm_core.h Removed initialization of the global waiting_fds list.
vm.c Removed calls to rb_vm_memsize_waiting_fds in memory size reporting.
thread.c Commented out and removed waiting_fd structures/functions in favor of per‑IO nodes.
test/-ext-/thread_fd/test_thread_fd_close.rb Removed outdated test file no longer applicable under the new design.
io.c Updated blocking call APIs to accept an IO pointer instead of a raw file descriptor.
internal/* Updated function declarations and struct fields to reflect per‑IO blocking operations.
Files not reviewed (1)
  • ext/-test-/thread_fd/depend: Language not supported
Comments suppressed due to low confidence (2)

vm.c:3229

  • Verify that the new per‑IO blocking operations are correctly accounted for in the overall memory size calculations now that the global waiting_fd memory size contribution has been removed.
-        rb_vm_memsize_waiting_fds(&vm->waiting_fds) +

test/-ext-/thread_fd/test_thread_fd_close.rb:1

  • The removal of the thread_fd close test file reduces test coverage for FD closing behavior; please ensure that the per‑IO waiting/blocking operations introduced in this PR are adequately covered by other tests.
Entire file removed

@ioquatix ioquatix marked this pull request as draft April 18, 2025 01:09
@ioquatix ioquatix force-pushed the io-close-isolated branch 3 times, most recently from 9cba6ac to 5d85916 Compare April 18, 2025 01:25
@ioquatix ioquatix force-pushed the io-close-isolated branch 3 times, most recently from aa93db0 to a0aa5d2 Compare April 18, 2025 01:51

This comment has been minimized.

thread.c Outdated
if (rb_notify_fd_close(fd, &busy)) {
rb_notify_fd_close_wait(&busy);
}
/* No-op */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We found only one practical example in gems:

https://github.com/vitoshalabs/ruby-pcap/blob/master/ext/pcap/Pcap.c#L149

https://gist.github.com/ko1/81aa42d1bee240a8d8f8d431d165e0c0

not sure we need to consider about that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your investigation.

From your linked example, is it safe to call the current implementation of rb_thread_fd_close from within GC?

I think a better solution is to take a reference to an auto close IO instance. It is an easy fix and then the code can be updated to use rb_io_wait etc.

As an interim solution, what about issuing a warning and adding documentation about how to fix it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplified code (as I understand):

io1 = open(...)
io2 = IO.for_fd(io1.fileno) # https://github.com/vitoshalabs/ruby-pcap/blob/master/ext/pcap/Pcap.c#L455C94-L455C95

# maybe io2 is used
io2.read...

rb_notify_fd_close_wait(io1.fileno) # wait for closing interrupting io2

Copy link
Member Author

@ioquatix ioquatix May 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using RUBY_IO_MODE_EXTERNAL and rb_io_close is a much better model for us to expose as a public interface:

io1 = open(...)
io2 = IO.for_fd(io1.fileno, FMODE_EXTERNAL)

# maybe io2 is used
io2.read...

rb_io_close(io2)

I've updated the documentation to recommend this.

@ioquatix ioquatix force-pushed the io-close-isolated branch from 402685f to cbe7c95 Compare May 3, 2025 07:47
@ioquatix ioquatix requested review from ko1 and Copilot May 3, 2025 10:47
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request refactors how waiting file descriptors are managed by removing the global waiting_fds list and transitioning to per-IO blocking operation mechanisms. Key changes include eliminating waiting_fds initialization and related memsize computations, overhauling thread and IO blocking logic in thread.c and io.c, and updating tests and deprecated extensions accordingly.

Reviewed Changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated no comments.

Show a summary per file
File Description
vm_core.h Removes the initialization of the global waiting_fds list to support a per-IO approach.
vm.c Eliminates the waiting_fds memsize function and its usage in the overall VM memsize calculation.
thread.c Removes the waiting_fd structure and associated functions, replacing them with a new blocking operation release.
test/ruby/test_io.rb Adjusts the range in a loop to properly limit file descriptor usage.
io.c Updates finalization logic by removing the busy parameter and replacing calls with the new IO close-wait functions.
internal/thread.h & internal/io.h Remove deprecated APIs and type definitions related to waiting_fd.
test/-ext-/thread_fd/* & ext/-test-/thread_fd/extconf.rb Remove deprecated thread_fd extension tests and associated build files.
Files not reviewed (2)
  • common.mk: Language not supported
  • ext/-test-/thread_fd/depend: Language not supported
Comments suppressed due to low confidence (5)

vm_core.h:1870

  • The removal of the waiting_fds initialization appears intentional for per-IO handling. Please verify that all parts of the codebase referencing waiting_fds have been updated to use the new per-IO blocking operations.
ccan_list_head_init(&vm->waiting_fds);

vm.c:3196

  • The removal of the rb_vm_memsize_waiting_fds declaration and its usage in the memsize calculation must be coordinated with the new per-IO mechanism. Confirm that the overall memsize reporting remains accurate after these changes.
size_t rb_vm_memsize_waiting_fds(struct ccan_list_head *waiting_fds); // thread.c

thread.c:151

  • The waiting_fd structure and its associated functions have been removed in favor of a new blocking operation approach. Please ensure that the new mechanism (using rb_io_blocking_operation_release and related logic) correctly handles concurrency and interrupt processing.
struct waiting_fd {

test/ruby/test_io.rb:3829

  • Changing the range from (0..fd_setsize+1) to (0...fd_setsize) ensures that file descriptors do not exceed the set size. Confirm that this correction aligns with the intended limits of the fd set.
(0...fd_setsize).map {|i|

io.c:5520

  • The updated fptr_finalize_flush signature (removing the busy parameter) reflects the new IO close logic. Verify that the new function correctly waits for blocking operations to complete before closing the IO and that all call sites are updated accordingly.
fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl)

@ioquatix ioquatix force-pushed the io-close-isolated branch from 345c156 to c6d1db9 Compare May 13, 2025 07:40
@ioquatix
Copy link
Member Author

ioquatix commented May 13, 2025

@ko1 requested the following changes:

  • Fix common.mk dependencies.
  • Remove verbose warning details.
  • Update NEWS.
  • Remove mutex synchronisation around thread_io_close_notify_all.

@ioquatix ioquatix force-pushed the io-close-isolated branch from 6fbf43e to 8993485 Compare May 13, 2025 07:54
@ioquatix ioquatix merged commit 425fa0a into ruby:master May 13, 2025
81 checks passed
@ioquatix ioquatix deleted the io-close-isolated branch May 13, 2025 10:02
@ntkme
Copy link
Contributor

ntkme commented May 14, 2025

@ioquatix I think this change is causing segmentation fault in my nightly CI run against ruby head:

https://github.com/sass-contrib/sass-embedded-host-ruby/actions/runs/15010437636/job/42178042427

To summarize the use case: sass-embedded spawns the dart-sass child process and communicates with child process’ stdio. When parent ruby process is forked, the stdio file descriptors of the child process are duplicated. However, because this gem does not support share the child process across multiple parent process, I need to close the stdio file descriptors in the forked ruby process. Right now it crashes when closing the duplicated file descriptors right after fork in the forked ruby process.

@ioquatix
Copy link
Member Author

Thanks for your report, I will take a look.

@ioquatix
Copy link
Member Author

I was able to reproduce the issue, I'll work on a fix.

@ioquatix
Copy link
Member Author

Okay, I found the root cause of the issue.

@ioquatix
Copy link
Member Author

Here is the reproduction:

r, w = IO.pipe

thread = Thread.new do
  r.read
end

Thread.pass until thread.status == "sleep"

fork do
  r.close
end

w.close

Process.wait
thread.join

@ioquatix
Copy link
Member Author

Proposed fix: #13343

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants