Skip to content

Commit 465d378

Browse files
committed
Fix console cursor on BSDs in async context
`IO.console.cursor` errors on kqueue-based systems when used with the fiber scheduler. ```ruby require 'async' Async { IO.console(:cursor) }.wait ``` This works on Linux but raises `Errno::EINVAL: Invalid argument` with `IO_Event_Selector_KQueue_Waiting_register` on macOS. I don't know if it's an acceptable fix, but this patch just dups file descriptors for stdin/stdout/stderr if detected alongside kqueue.
1 parent 6022837 commit 465d378

File tree

2 files changed

+27
-4
lines changed

2 files changed

+27
-4
lines changed

ext/io/console/console.c

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ extern VALUE rb_scheduler_timeout(struct timeval *timeout);
9696
# define rb_fiber_scheduler_make_timeout rb_scheduler_timeout
9797
#endif
9898

99+
#ifdef HAVE_SYS_EVENT_H
100+
extern VALUE rb_fiber_scheduler_current(void);
101+
#endif
102+
99103
#ifndef HAVE_RB_IO_DESCRIPTOR
100104
static int
101105
io_descriptor_fallback(VALUE io)
@@ -1695,12 +1699,30 @@ console_dev(int argc, VALUE *argv, VALUE klass)
16951699
int fd;
16961700
VALUE path = rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE));
16971701

1702+
fd = -1;
1703+
#ifdef HAVE_SYS_EVENT_H
1704+
VALUE scheduler = rb_fiber_scheduler_current();
1705+
if (!NIL_P(scheduler)) {
1706+
if (isatty(0)) {
1707+
fd = dup(0);
1708+
path = rb_obj_freeze(rb_str_new2("<STDIN>"));
1709+
} else if (isatty(1)) {
1710+
fd = dup(1);
1711+
path = rb_obj_freeze(rb_str_new2("<STDOUT>"));
1712+
} else if (isatty(2)) {
1713+
fd = dup(2);
1714+
path = rb_obj_freeze(rb_str_new2("<STDERR>"));
1715+
}
1716+
}
1717+
#endif
1718+
if (fd == -1) {
16981719
#ifdef CONSOLE_DEVICE_FOR_WRITING
1699-
fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_WRITING, O_RDWR, 0);
1700-
if (fd < 0) return Qnil;
1701-
out = rb_io_open_descriptor(klass, fd, FMODE_WRITABLE | FMODE_SYNC, path, Qnil, NULL);
1720+
int writing_fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_WRITING, O_RDWR, 0);
1721+
if (writing_fd < 0) return Qnil;
1722+
out = rb_io_open_descriptor(klass, writing_fd, FMODE_WRITABLE | FMODE_SYNC, path, Qnil, NULL);
17021723
#endif
1703-
fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_READING, O_RDWR, 0);
1724+
fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_READING, O_RDWR, 0);
1725+
}
17041726
if (fd < 0) {
17051727
#ifdef CONSOLE_DEVICE_FOR_WRITING
17061728
rb_io_close(out);

ext/io/console/extconf.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
case ok
4242
when true
4343
have_header("sys/ioctl.h") if hdr
44+
have_header("sys/event.h")
4445
# rb_check_hash_type: 1.9.3
4546
# rb_io_get_write_io: 1.9.1
4647
# rb_cloexec_open: 2.0.0

0 commit comments

Comments
 (0)