summaryrefslogtreecommitdiff
path: root/io.c
diff options
context:
space:
mode:
authornormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-04-21 03:12:36 (GMT)
committernormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-04-21 03:12:36 (GMT)
commit645f7fbd4ec0199c6266df19ad82d99bdd8553a8 (patch)
tree9bf8bba9938ce3c535bd59ef3f5db2f82fc1f421 /io.c
parentaf72dcd91b70e94b0f8299ab0464dafe884d2695 (diff)
io.c: do not use rb_notify_fd_close close on recycled FD
It is unsafe to release GVL and call rb_notify_fd_close after close(2) on any given FD. FDs (file descriptor) may be recycled in other threads immediately after close() to point to a different file description. Note the distinction between "file description" and "file descriptor". th-1 | th-2 -------------------------------+--------------------------------------- io_close_fptr | rb_notify_fd_close(fd) | fptr_finalize_flush | close(fd) | rb_thread_schedule | | fd reused (via pipe/open/socket/etc) rb_notify_fd_close(fd) | | sees "stream closed" exception | for DIFFERENT file description * thread.c (rb_thread_io_blocking_region): adjust comment for list_del * thread.c (rb_notify_fd_close): give busy list to caller * thread.c (rb_thread_fd_close): loop on busy list * io.c (io_close_fptr): do not call rb_thread_fd_close on invalid FD * io.c (io_reopen): use rb_thread_fd_close Fixes: r57422 ("io.c: close before wait") git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63216 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'io.c')
-rw-r--r--io.c16
1 files changed, 7 insertions, 9 deletions
diff --git a/io.c b/io.c
index ebf5743..dfe021a 100644
--- a/io.c
+++ b/io.c
@@ -21,6 +21,7 @@
#include <ctype.h>
#include <errno.h>
#include "ruby_atomic.h"
+#include "ccan/list/list.h"
#undef free
#define free(x) xfree(x)
@@ -4654,15 +4655,14 @@ rb_io_memsize(const rb_io_t *fptr)
# define KEEPGVL FALSE
#endif
-int rb_notify_fd_close(int fd);
+int rb_notify_fd_close(int fd, struct list_head *);
static rb_io_t *
io_close_fptr(VALUE io)
{
rb_io_t *fptr;
- int fd;
VALUE write_io;
rb_io_t *write_fptr;
- int busy;
+ LIST_HEAD(busy);
write_io = GetWriteIO(io);
if (io != write_io) {
@@ -4676,11 +4676,9 @@ io_close_fptr(VALUE io)
if (!fptr) return 0;
if (fptr->fd < 0) return 0;
- fd = fptr->fd;
- busy = rb_notify_fd_close(fd);
- if (busy) {
- fptr_finalize_flush(fptr, FALSE, KEEPGVL);
- do rb_thread_schedule(); while (rb_notify_fd_close(fd));
+ if (rb_notify_fd_close(fptr->fd, &busy)) {
+ fptr_finalize_flush(fptr, FALSE, KEEPGVL); /* calls close(fptr->fd) */
+ do rb_thread_schedule(); while (!list_empty(&busy));
}
rb_io_fptr_cleanup(fptr, FALSE);
return fptr;
@@ -7185,7 +7183,7 @@ io_reopen(VALUE io, VALUE nfile)
rb_update_max_fd(fd);
fptr->fd = fd;
}
- rb_notify_fd_close(fd);
+ rb_thread_fd_close(fd);
if ((orig->mode & FMODE_READABLE) && pos >= 0) {
if (io_seek(fptr, pos, SEEK_SET) < 0 && errno) {
rb_sys_fail_path(fptr->pathv);