Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ext/io/event/selector/epoll.c
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,8 @@ void * select_internal(void *_arguments) {

static
void select_internal_without_gvl(struct select_arguments *arguments) {
IO_Event_Selector_before_waiting(arguments->selector);

arguments->selector->blocked = 1;
rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
arguments->selector->blocked = 0;
Expand Down
2 changes: 2 additions & 0 deletions ext/io/event/selector/kqueue.c
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,8 @@ void * select_internal(void *_arguments) {

static
void select_internal_without_gvl(struct select_arguments *arguments) {
IO_Event_Selector_before_waiting(arguments->selector);

arguments->selector->blocked = 1;

rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
Expand Down
10 changes: 10 additions & 0 deletions ext/io/event/selector/selector.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ static VALUE IO_Event_Selector_nonblock(VALUE class, VALUE io)
return rb_ensure(rb_yield, io, IO_Event_Selector_nonblock_ensure, (VALUE)&arguments);
}

static float IO_Event_Selector_GARBAGE_COLLECTION = -1;

void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
#ifndef HAVE_RB_IO_DESCRIPTOR
id_fileno = rb_intern("fileno");
Expand All @@ -89,6 +91,11 @@ void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
rb_gc_register_mark_object(rb_Process_Status);
#endif

char *garbage_collection_env = getenv("IO_EVENT_SELECTOR_GARBAGE_COLLECTION");
if (garbage_collection_env) {
IO_Event_Selector_GARBAGE_COLLECTION = atof(garbage_collection_env);
}

rb_define_singleton_method(IO_Event_Selector, "nonblock", IO_Event_Selector_nonblock, 1);
}

Expand All @@ -98,6 +105,9 @@ void IO_Event_Selector_initialize(struct IO_Event_Selector *backend, VALUE self,

backend->waiting = NULL;
backend->ready = NULL;

backend->garbage_collection = IO_Event_Selector_GARBAGE_COLLECTION;
backend->last_garbage_collection = (struct timespec){0, 0};
}

VALUE IO_Event_Selector_loop_resume(struct IO_Event_Selector *backend, VALUE fiber, int argc, VALUE *argv) {
Expand Down
17 changes: 17 additions & 0 deletions ext/io/event/selector/selector.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,27 @@ struct IO_Event_Selector {
struct IO_Event_Selector_Queue *waiting;
// Process from ready (back/tail of queue).
struct IO_Event_Selector_Queue *ready;

// Minimum interval in seconds between out-of-band garbage collections, or < 0 to disable:
float garbage_collection;
struct timespec last_garbage_collection;
};

void IO_Event_Selector_initialize(struct IO_Event_Selector *backend, VALUE self, VALUE loop);

static inline IO_Event_Selector_before_waiting(struct IO_Event_Selector *backend) {
// Experimental support for out-of-band garbage collection. This allows the selector to perform garbage collection at regular intervals while waiting for events, which can help reduce overall latency.
if (backend->garbage_collection >= 0) {
struct timespec now;
IO_Event_Time_current(&now);

if (IO_Event_Time_delta(&backend->last_garbage_collection, &now) >= backend->garbage_collection) {
rb_gc();
backend->last_garbage_collection = now;
}
}
}

static inline
void IO_Event_Selector_mark(struct IO_Event_Selector *backend) {
rb_gc_mark_movable(backend->self);
Expand Down
2 changes: 2 additions & 0 deletions ext/io/event/selector/uring.c
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,8 @@ static
int select_internal_without_gvl(struct select_arguments *arguments) {
io_uring_submit_flush(arguments->selector);

IO_Event_Selector_before_waiting(arguments->selector);

arguments->selector->blocked = 1;
rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
arguments->selector->blocked = 0;
Expand Down
4 changes: 4 additions & 0 deletions releases.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Releases

## Unreleased

- **Experimental** Add support for out-of-band garbage collection. When enabled via the `IO_EVENT_SELECTOR_GARBAGE_COLLECTION` environment variable, the selector will run garbage collection at a minimum interval (in seconds) while the event loop is blocking, reducing GC pressure during active I/O. A negative value (default) disables the feature; `0` runs GC on every block.

## v1.14.4

- Allow `epoll_pwait2` to be disabled via `--disable-epoll_pwait2`.
Expand Down
Loading