From 85d2e89ac2de41d3f494a49404759e671f22e47c Mon Sep 17 00:00:00 2001
From: Michal Suchanek <msuchanek@suse.de>
Date: Mon, 3 Feb 2020 19:26:58 +0100
Subject: [PATCH 03/12] select_internal: add additional argument for poll()
 error fd list

Signed-off-by: Michal Suchanek <msuchanek@suse.de>
---
 include/ruby/internal/intern/select.h |  2 +-
 io.c                                  | 57 +++++++++++++++++++++++----
 2 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/include/ruby/internal/intern/select.h b/include/ruby/internal/intern/select.h
index 43d4cf354cd7..9d07b3503d76 100644
--- a/include/ruby/internal/intern/select.h
+++ b/include/ruby/internal/intern/select.h
@@ -45,7 +45,7 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
 
 struct timeval;
 
-int rb_thread_fd_select(int, rb_fdset_t *, rb_fdset_t *, rb_fdset_t *, struct timeval *);
+int rb_thread_fd_select(int, rb_fdset_t *, rb_fdset_t *, rb_fdset_t *, rb_fdset_t *, struct timeval *);
 
 RBIMPL_SYMBOL_EXPORT_END()
 
diff --git a/io.c b/io.c
index 3ad322351dd2..0131b0e8eb8d 100644
--- a/io.c
+++ b/io.c
@@ -9152,7 +9152,7 @@ rb_f_backquote(VALUE obj, VALUE str)
 #endif
 
 static VALUE
-select_internal(VALUE read, VALUE write, VALUE except, struct timeval *tp, rb_fdset_t *fds)
+select_internal(VALUE read, VALUE write, VALUE except, VALUE error, struct timeval *tp, rb_fdset_t *fds)
 {
     VALUE res, list;
     rb_fdset_t *rp, *wp, *ep, *erp;
@@ -9169,7 +9169,7 @@ select_internal(VALUE read, VALUE write, VALUE except, struct timeval *tp, rb_fd
 	    rb_fd_set(fptr->fd, &fds[0]);
 	    if (READ_DATA_PENDING(fptr) || READ_CHAR_PENDING(fptr)) { /* check for buffered data */
 		pending++;
-		rb_fd_set(fptr->fd, &fds[3]);
+		rb_fd_set(fptr->fd, &fds[4]);
 	    }
 	    if (max < fptr->fd) max = fptr->fd;
 	}
@@ -9215,7 +9215,25 @@ select_internal(VALUE read, VALUE write, VALUE except, struct timeval *tp, rb_fd
 	ep = 0;
     }
 
-    erp = 0;
+    if (!NIL_P(error)) {
+	Check_Type(error, T_ARRAY);
+	for (i=0; i<RARRAY_LEN(error); i++) {
+            VALUE io = rb_io_get_io(RARRAY_AREF(error, i));
+            VALUE write_io = GetWriteIO(io);
+	    GetOpenFile(io, fptr);
+	    rb_fd_set(fptr->fd, &fds[3]);
+	    if (max < fptr->fd) max = fptr->fd;
+            if (io != write_io) {
+                GetOpenFile(write_io, fptr);
+                rb_fd_set(fptr->fd, &fds[3]);
+                if (max < fptr->fd) max = fptr->fd;
+            }
+	}
+	erp = &fds[3];
+    }
+    else {
+	erp = 0;
+    }
 
     max++;
 
@@ -9225,10 +9243,11 @@ select_internal(VALUE read, VALUE write, VALUE except, struct timeval *tp, rb_fd
     }
     if (!pending && n == 0) return Qnil; /* returns nil on timeout */
 
-    res = rb_ary_new2(3);
+    res = rb_ary_new2(4);
     rb_ary_push(res, rp?rb_ary_new():rb_ary_new2(0));
     rb_ary_push(res, wp?rb_ary_new():rb_ary_new2(0));
     rb_ary_push(res, ep?rb_ary_new():rb_ary_new2(0));
+    rb_ary_push(res, erp?rb_ary_new():rb_ary_new2(0));
 
     if (rp) {
 	list = RARRAY_AREF(res, 0);
@@ -9237,7 +9256,7 @@ select_internal(VALUE read, VALUE write, VALUE except, struct timeval *tp, rb_fd
 	    VALUE io = rb_io_get_io(obj);
 	    GetOpenFile(io, fptr);
 	    if (rb_fd_isset(fptr->fd, &fds[0]) ||
-		rb_fd_isset(fptr->fd, &fds[3])) {
+		rb_fd_isset(fptr->fd, &fds[4])) {
 		rb_ary_push(list, obj);
 	    }
 	}
@@ -9275,6 +9294,25 @@ select_internal(VALUE read, VALUE write, VALUE except, struct timeval *tp, rb_fd
 	}
     }
 
+    if (erp) {
+	list = RARRAY_AREF(res, 3);
+	for (i=0; i< RARRAY_LEN(error); i++) {
+	    VALUE obj = rb_ary_entry(error, i);
+	    VALUE io = rb_io_get_io(obj);
+	    VALUE write_io = GetWriteIO(io);
+	    GetOpenFile(io, fptr);
+	    if (rb_fd_isset(fptr->fd, &fds[3])) {
+		rb_ary_push(list, obj);
+	    }
+	    else if (io != write_io) {
+		GetOpenFile(write_io, fptr);
+		if (rb_fd_isset(fptr->fd, &fds[3])) {
+		    rb_ary_push(list, obj);
+		}
+	    }
+	}
+    }
+
     return res;			/* returns an empty array on interrupt */
 }
 
@@ -9289,7 +9327,7 @@ select_call(VALUE arg)
 {
     struct select_args *p = (struct select_args *)arg;
 
-    return select_internal(p->read, p->write, p->except, p->timeout, p->fdsets);
+    return select_internal(p->read, p->write, p->except, p->error, p->timeout, p->fdsets);
 }
 
 static VALUE
@@ -9620,7 +9658,7 @@ rb_io_advise(int argc, VALUE *argv, VALUE io)
 static VALUE
 rb_f_select(int argc, VALUE *argv, VALUE obj)
 {
-    VALUE timeout;
+    VALUE timeout, rv;
     struct select_args args;
     struct timeval timerec;
     int i;
@@ -9638,7 +9676,10 @@ rb_f_select(int argc, VALUE *argv, VALUE obj)
     for (i = 0; i < numberof(args.fdsets); ++i)
 	rb_fd_init(&args.fdsets[i]);
 
-    return rb_ensure(select_call, (VALUE)&args, select_end, (VALUE)&args);
+    rv = rb_ensure(select_call, (VALUE)&args, select_end, (VALUE)&args);
+    if (RB_TYPE_P(rv, T_ARRAY))
+        rb_ary_pop(rv);
+    return rv;
 }
 
 #if (defined(__linux__) && !defined(__ANDROID__)) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
-- 
2.26.2

