From b74c98d0bc6e42c605e4a602a493f9f25961fa7b Mon Sep 17 00:00:00 2001
From: Michal Suchanek <msuchanek@suse.de>
Date: Mon, 3 Feb 2020 18:42:18 +0100
Subject: [PATCH 02/12] io: add additional argument to *fd_select calls

This extra argument will be used to support poll() error fd list.

Signed-off-by: Michal Suchanek <msuchanek@suse.de>
---
 .../ruby/internal/intern/select/largesize.h   |  2 +-
 include/ruby/internal/intern/select/win32.h   |  2 +-
 io.c                                          | 15 ++++++-----
 thread.c                                      | 25 ++++++++++++++-----
 thread_pthread.c                              |  4 +--
 5 files changed, 32 insertions(+), 16 deletions(-)

diff --git a/include/ruby/internal/intern/select/largesize.h b/include/ruby/internal/intern/select/largesize.h
index ba56a159b1e8..7d75c4718a0b 100644
--- a/include/ruby/internal/intern/select/largesize.h
+++ b/include/ruby/internal/intern/select/largesize.h
@@ -80,7 +80,7 @@ void rb_fd_clr(int, rb_fdset_t *);
 int rb_fd_isset(int, const rb_fdset_t *);
 void rb_fd_copy(rb_fdset_t *, const fd_set *, int);
 void rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src);
-int rb_fd_select(int, rb_fdset_t *, rb_fdset_t *, rb_fdset_t *, struct timeval *);
+int rb_fd_select(int, rb_fdset_t *, rb_fdset_t *, rb_fdset_t *, rb_fdset_t *, struct timeval *);
 RBIMPL_SYMBOL_EXPORT_END()
 
 RBIMPL_ATTR_NONNULL(())
diff --git a/include/ruby/internal/intern/select/win32.h b/include/ruby/internal/intern/select/win32.h
index ef75a0f76007..e63bbfd62af4 100644
--- a/include/ruby/internal/intern/select/win32.h
+++ b/include/ruby/internal/intern/select/win32.h
@@ -91,7 +91,7 @@ rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
 }
 
 static inline int
-rb_fd_select(int n, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout)
+rb_fd_select(int n, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, rb_fdset_t *errfds, struct timeval *timeout)
 {
     return rb_w32_select(
         n,
diff --git a/io.c b/io.c
index b3f5e5ef399e..3ad322351dd2 100644
--- a/io.c
+++ b/io.c
@@ -9155,7 +9155,7 @@ static VALUE
 select_internal(VALUE read, VALUE write, VALUE except, struct timeval *tp, rb_fdset_t *fds)
 {
     VALUE res, list;
-    rb_fdset_t *rp, *wp, *ep;
+    rb_fdset_t *rp, *wp, *ep, *erp;
     rb_io_t *fptr;
     long i;
     int max = 0, n;
@@ -9215,9 +9215,11 @@ select_internal(VALUE read, VALUE write, VALUE except, struct timeval *tp, rb_fd
 	ep = 0;
     }
 
+    erp = 0;
+
     max++;
 
-    n = rb_thread_fd_select(max, rp, wp, ep, tp);
+    n = rb_thread_fd_select(max, rp, wp, ep, erp, tp);
     if (n < 0) {
 	rb_sys_fail(0);
     }
@@ -9277,9 +9279,9 @@ select_internal(VALUE read, VALUE write, VALUE except, struct timeval *tp, rb_fd
 }
 
 struct select_args {
-    VALUE read, write, except;
+    VALUE read, write, except, error;
     struct timeval *timeout;
-    rb_fdset_t fdsets[4];
+    rb_fdset_t fdsets[5];
 };
 
 static VALUE
@@ -9624,6 +9626,7 @@ rb_f_select(int argc, VALUE *argv, VALUE obj)
     int i;
 
     rb_scan_args(argc, argv, "13", &args.read, &args.write, &args.except, &timeout);
+    args.error = Qnil;
     if (NIL_P(timeout)) {
 	args.timeout = 0;
     }
@@ -10937,10 +10940,10 @@ nogvl_wait_for_single_fd(int fd, short events)
 
     switch (events) {
       case RB_WAITFD_IN:
-        ret = rb_fd_select(fd + 1, &fds, 0, 0, 0);
+        ret = rb_fd_select(fd + 1, &fds, 0, 0, 0, 0);
         break;
       case RB_WAITFD_OUT:
-        ret = rb_fd_select(fd + 1, 0, &fds, 0, 0);
+        ret = rb_fd_select(fd + 1, 0, &fds, 0, 0, 0);
         break;
       default:
         VM_UNREACHABLE(nogvl_wait_for_single_fd);
diff --git a/thread.c b/thread.c
index 54c65a7d57a9..9681e75464f9 100644
--- a/thread.c
+++ b/thread.c
@@ -3868,7 +3868,7 @@ rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
 }
 
 int
-rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout)
+rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, rb_fdset_t *errorfds, struct timeval *timeout)
 {
     fd_set *r = NULL, *w = NULL, *e = NULL;
     if (readfds) {
@@ -3883,6 +3883,8 @@ rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *excep
         rb_fd_resize(n - 1, exceptfds);
         e = rb_fd_ptr(exceptfds);
     }
+    if (errorfds)
+        rb_fd_zero(errorfds);
     return select(n, r, w, e, timeout);
 }
 
@@ -3995,9 +3997,11 @@ struct select_set {
     rb_fdset_t *rset;
     rb_fdset_t *wset;
     rb_fdset_t *eset;
+    rb_fdset_t *errset;
     rb_fdset_t orig_rset;
     rb_fdset_t orig_wset;
     rb_fdset_t orig_eset;
+    rb_fdset_t orig_errset;
     struct timeval *timeout;
 };
 
@@ -4014,6 +4018,7 @@ select_set_free(VALUE p)
     rb_fd_term(&set->orig_rset);
     rb_fd_term(&set->orig_wset);
     rb_fd_term(&set->orig_eset);
+    rb_fd_term(&set->orig_errset);
 
     return Qfalse;
 }
@@ -4048,6 +4053,7 @@ do_select(VALUE p)
     (restore_fdset(set->rset, &set->orig_rset), \
      restore_fdset(set->wset, &set->orig_wset), \
      restore_fdset(set->eset, &set->orig_eset), \
+     restore_fdset(set->errset, &set->orig_errset), \
      TRUE)
 
     do {
@@ -4061,7 +4067,7 @@ do_select(VALUE p)
             sto = sigwait_timeout(set->th, set->sigwait_fd, to, &drained);
             if (!RUBY_VM_INTERRUPTED(set->th->ec)) {
                 result = native_fd_select(set->max, set->rset, set->wset,
-                                          set->eset,
+                                          set->eset, set->errset,
                                           rb_hrtime2timeval(&tv, sto), set->th);
                 if (result < 0) lerrno = errno;
             }
@@ -4133,7 +4139,7 @@ init_set_fd(int fd, rb_fdset_t *fds)
 
 int
 rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t * except,
-		    struct timeval *timeout)
+		    rb_fdset_t * error, struct timeval *timeout)
 {
     struct select_set set;
 
@@ -4143,9 +4149,10 @@ rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t *
     set.rset = read;
     set.wset = write;
     set.eset = except;
+    set.errset = error;
     set.timeout = timeout;
 
-    if (!set.rset && !set.wset && !set.eset) {
+    if (!set.rset && !set.wset && !set.eset && !set.errset) {
         if (!timeout) {
             rb_thread_sleep_forever();
             return 0;
@@ -4178,6 +4185,7 @@ rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t *
     fd_init_copy(rset);
     fd_init_copy(wset);
     fd_init_copy(eset);
+    fd_init_copy(errset);
 #undef fd_init_copy
 
     return (int)rb_ensure(do_select, (VALUE)&set, select_set_free, (VALUE)&set);
@@ -4308,6 +4316,7 @@ struct select_args {
     rb_fdset_t *read;
     rb_fdset_t *write;
     rb_fdset_t *except;
+    rb_fdset_t *error;
     struct waiting_fd wfd;
     struct timeval *tv;
 };
@@ -4319,7 +4328,7 @@ select_single(VALUE ptr)
     int r;
 
     r = rb_thread_fd_select(args->as.fd + 1,
-                            args->read, args->write, args->except, args->tv);
+                            args->read, args->write, args->except, args->error, args->tv);
     if (r == -1)
 	args->as.error = errno;
     if (r > 0) {
@@ -4330,6 +4339,8 @@ select_single(VALUE ptr)
 	    r |= RB_WAITFD_OUT;
 	if (args->except && rb_fd_isset(args->as.fd, args->except))
 	    r |= RB_WAITFD_PRI;
+	if (args->error && rb_fd_isset(args->as.fd, args->error))
+	    r |= RB_WAITFD_ERR;
     }
     return (VALUE)r;
 }
@@ -4343,6 +4354,7 @@ select_single_cleanup(VALUE ptr)
     if (args->read) rb_fd_term(args->read);
     if (args->write) rb_fd_term(args->write);
     if (args->except) rb_fd_term(args->except);
+    if (args->error) rb_fd_term(args->error);
 
     return (VALUE)-1;
 }
@@ -4358,7 +4370,7 @@ rb_wait_for_single_fd(int fd, int events, struct timeval *timeout)
         return RTEST(result);
     }
 
-    rb_fdset_t rfds, wfds, efds;
+    rb_fdset_t rfds, wfds, efds, errfds;
     struct select_args args;
     int r;
     VALUE ptr = (VALUE)&args;
@@ -4367,6 +4379,7 @@ rb_wait_for_single_fd(int fd, int events, struct timeval *timeout)
     args.read = (events & RB_WAITFD_IN) ? init_set_fd(fd, &rfds) : NULL;
     args.write = (events & RB_WAITFD_OUT) ? init_set_fd(fd, &wfds) : NULL;
     args.except = (events & RB_WAITFD_PRI) ? init_set_fd(fd, &efds) : NULL;
+    args.error = (events & RB_WAITFD_ERR) ? init_set_fd(fd, &errfds) : NULL;
     args.tv = timeout;
     args.wfd.fd = fd;
     args.wfd.th = GET_THREAD();
diff --git a/thread_pthread.c b/thread_pthread.c
index ee2f7bc90912..66f4f480cd56 100644
--- a/thread_pthread.c
+++ b/thread_pthread.c
@@ -1156,9 +1156,9 @@ native_thread_apply_priority(rb_thread_t *th)
 #endif /* USE_NATIVE_THREAD_PRIORITY */
 
 static int
-native_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout, rb_thread_t *th)
+native_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, rb_fdset_t *errorfds, struct timeval *timeout, rb_thread_t *th)
 {
-    return rb_fd_select(n, readfds, writefds, exceptfds, timeout);
+    return rb_fd_select(n, readfds, writefds, exceptfds, errorfds, timeout);
 }
 
 static void
-- 
2.26.2

