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
14 changes: 14 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,19 @@ The following bundled gems are updated.

## C API updates

* IO

* `rb_thread_fd_close` is deprecated and now a no-op. If you need to expose
file descriptors from C extensions to Ruby code, create an `IO` instance
using `RUBY_IO_MODE_EXTERNAL` and use `rb_io_close(io)` to close it (this
also interrupts and waits for all pending operations on the `IO`
instance). Directly closing file descriptors does not interrupt pending
operations, and may lead to undefined beahviour. In other words, if two
`IO` objects share the same file descriptor, closing one does not affect
the other. [[Feature #18455]]

[[Feature #18455]]

## Implementation improvements

## JIT
Expand All @@ -112,3 +125,4 @@ The following bundled gems are updated.
[Bug #21049]: https://bugs.ruby-lang.org/issues/21049
[Feature #21216]: https://bugs.ruby-lang.org/issues/21216
[Feature #21258]: https://bugs.ruby-lang.org/issues/21258
[Feature #18455]: https://bugs.ruby-lang.org/issues/18455
161 changes: 0 additions & 161 deletions ext/-test-/thread_fd/depend

This file was deleted.

2 changes: 0 additions & 2 deletions ext/-test-/thread_fd/extconf.rb

This file was deleted.

30 changes: 0 additions & 30 deletions ext/-test-/thread_fd/thread_fd.c

This file was deleted.

3 changes: 3 additions & 0 deletions gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -3204,6 +3204,7 @@ rb_gc_mark_children(void *objspace, VALUE obj)
gc_mark_internal(RFILE(obj)->fptr->encs.ecopts);
gc_mark_internal(RFILE(obj)->fptr->write_lock);
gc_mark_internal(RFILE(obj)->fptr->timeout);
gc_mark_internal(RFILE(obj)->fptr->wakeup_mutex);
}
break;

Expand Down Expand Up @@ -4185,6 +4186,8 @@ rb_gc_update_object_references(void *objspace, VALUE obj)
UPDATE_IF_MOVED(objspace, RFILE(obj)->fptr->writeconv_pre_ecopts);
UPDATE_IF_MOVED(objspace, RFILE(obj)->fptr->encs.ecopts);
UPDATE_IF_MOVED(objspace, RFILE(obj)->fptr->write_lock);
UPDATE_IF_MOVED(objspace, RFILE(obj)->fptr->timeout);
UPDATE_IF_MOVED(objspace, RFILE(obj)->fptr->wakeup_mutex);
}
break;
case T_REGEXP:
Expand Down
8 changes: 4 additions & 4 deletions include/ruby/internal/intern/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ int rb_thread_wait_fd(int fd);
int rb_thread_fd_writable(int fd);

/**
* Notifies a closing of a file descriptor to other threads. Multiple threads
* can wait for the given file descriptor at once. If such file descriptor is
* closed, threads need to start propagating their exceptions. This is the API
* to kick that process.
* This funciton is now a no-op. It was previously used to interrupt threads
* that were using the given file descriptor and wait for them to finish.
*
* @deprecated Use IO with RUBY_IO_MODE_EXTERNAL and `rb_io_close` instead.
*
* @param[in] fd A file descriptor.
* @note This function blocks until all the threads waiting for such fd
Expand Down
21 changes: 20 additions & 1 deletion internal/io.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,20 @@
struct rb_io;

#include "ruby/io.h" /* for rb_io_t */
#include "ccan/list/list.h"

#define IO_WITHOUT_GVL(func, arg) rb_nogvl(func, arg, RUBY_UBF_IO, 0, RB_NOGVL_OFFLOAD_SAFE)
#define IO_WITHOUT_GVL_INT(func, arg) (int)(VALUE)IO_WITHOUT_GVL(func, arg)

// Represents an in-flight blocking operation:
struct rb_io_blocking_operation {
// The linked list data structure.
struct ccan_list_node list;

// The execution context of the blocking operation:
struct rb_execution_context_struct *ec;
};

/** Ruby's IO, metadata and buffers. */
struct rb_io {

Expand Down Expand Up @@ -111,6 +121,15 @@ struct rb_io {
* The timeout associated with this IO when performing blocking operations.
*/
VALUE timeout;

/**
* Threads that are performing a blocking operation without the GVL using
* this IO. On calling IO#close, these threads will be interrupted so that
* the operation can be cancelled.
*/
struct ccan_list_head blocking_operations;
struct rb_execution_context_struct *closing_ec;
VALUE wakeup_mutex;
};

/* io.c */
Expand All @@ -125,7 +144,7 @@ VALUE rb_io_prep_stdin(void);
VALUE rb_io_prep_stdout(void);
VALUE rb_io_prep_stderr(void);

int rb_io_fptr_finalize(struct rb_io *fptr);
int rb_io_notify_close(struct rb_io *fptr);

RUBY_SYMBOL_EXPORT_BEGIN
/* io.c (export) */
Expand Down
11 changes: 3 additions & 8 deletions internal/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "ccan/list/list.h" /* for list in rb_io_close_wait_list */

struct rb_thread_struct; /* in vm_core.h */
struct rb_io;

#define RB_VM_SAVE_MACHINE_CONTEXT(th) \
do { \
Expand Down Expand Up @@ -58,14 +59,8 @@ void ruby_mn_threads_params(void);
int rb_thread_io_wait(struct rb_io *io, int events, struct timeval * timeout);
int rb_thread_wait_for_single_fd(int fd, int events, struct timeval * timeout);

struct rb_io_close_wait_list {
struct ccan_list_head pending_fd_users;
VALUE closing_thread;
VALUE closing_fiber;
VALUE wakeup_mutex;
};
int rb_notify_fd_close(int fd, struct rb_io_close_wait_list *busy);
void rb_notify_fd_close_wait(struct rb_io_close_wait_list *busy);
size_t rb_thread_io_close_interrupt(struct rb_io *);
void rb_thread_io_close_wait(struct rb_io *);

void rb_ec_check_ints(struct rb_execution_context_struct *ec);

Expand Down
Loading
Loading