From b4f0a68fba4b222461ca79feb94d94e325341129 Mon Sep 17 00:00:00 2001 From: Tomoyuki Sahara Date: Mon, 30 Sep 2013 10:17:21 +0900 Subject: add File#flock. --- README.md | 2 +- src/file.c | 35 +++++++++++++++++++++++++++++++++++ test/file.rb | 11 +++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e05577ef9..51b6d1851 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ DEALINGS IN THE SOFTWARE. | File#chmod | | | | File#chown | | | | File#ctime | | | -| File#flock | | | +| File#flock | o | | | File#lstat | | | | File#mtime | | | | File#path, File#to_path | o | | diff --git a/src/file.c b/src/file.c index 0fed5b21a..5c46aa965 100644 --- a/src/file.c +++ b/src/file.c @@ -36,6 +36,9 @@ #define STAT(p, s) stat(p, s) +extern mrb_value mrb_io_fileno(mrb_state *mrb, mrb_value io); + + mrb_value mrb_file_s_umask(mrb_state *mrb, mrb_value klass) { @@ -262,6 +265,36 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass) return mrb_str_new_cstr(mrb, home); } +mrb_value +mrb_file_flock(mrb_state *mrb, mrb_value self) +{ + mrb_int operation; + int fd; + + mrb_get_args(mrb, "i", &operation); + fd = mrb_fixnum(mrb_io_fileno(mrb, self)); + + while (flock(fd, operation) == -1) { + switch (errno) { + case EINTR: + /* retry */ + break; + case EAGAIN: /* NetBSD */ +#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: /* FreeBSD OpenBSD Linux */ +#endif + if (operation & LOCK_NB) { + return mrb_false_value(); + } + /* FALLTHRU - should not happen */ + default: + mrb_sys_fail(mrb, "flock failed"); + break; + } + } + return mrb_fixnum_value(0); +} + void mrb_init_file(mrb_state *mrb) { @@ -282,6 +315,8 @@ mrb_init_file(mrb_state *mrb) mrb_define_class_method(mrb, file, "_getwd", mrb_file__getwd, MRB_ARGS_NONE()); mrb_define_class_method(mrb, file, "_gethome", mrb_file__gethome, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, file, "flock", mrb_file_flock, MRB_ARGS_REQ(1)); + cnst = mrb_define_module_under(mrb, file, "Constants"); mrb_define_const(mrb, cnst, "LOCK_SH", mrb_fixnum_value(LOCK_SH)); mrb_define_const(mrb, cnst, "LOCK_EX", mrb_fixnum_value(LOCK_EX)); diff --git a/test/file.rb b/test/file.rb index 8fe7636e0..4b3458d1a 100644 --- a/test/file.rb +++ b/test/file.rb @@ -49,6 +49,17 @@ assert('File.extname') do assert_equal '', File.extname('.foo') end +assert('IO#flock') do + f = File.open $mrbtest_io_rfname + assert_equal(f.flock(File::LOCK_SH), 0) + assert_equal(f.flock(File::LOCK_UN), 0) + assert_equal(f.flock(File::LOCK_EX | File::LOCK_UN), 0) + assert_equal(f.flock(File::LOCK_UN), 0) + f.close + true +end + + assert('File.size') do File.size($mrbtest_io_rfname) == $mrbtest_io_msg.size + 1 and File.size($mrbtest_io_wfname) == 0 -- cgit v1.2.3 From d75d23294d31c3fea16f849a73823fd9d3f17dd9 Mon Sep 17 00:00:00 2001 From: Tomoyuki Sahara Date: Mon, 30 Sep 2013 10:36:29 +0900 Subject: fix descriptor leakage. --- src/io.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/io.c b/src/io.c index 8044874e9..284d90877 100644 --- a/src/io.c +++ b/src/io.c @@ -195,7 +195,8 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass) struct mrb_io *fptr; const char *pname; int pid, flags, fd, write_fd = -1; - int pr[2], pw[2]; + int pr[2] = { -1, -1 }; + int pw[2] = { -1, -1 }; int doexec; mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt); @@ -206,10 +207,13 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass) doexec = (strcmp("-", pname) != 0); - if (((flags & FMODE_READABLE) && pipe(pr) == -1) - || ((flags & FMODE_WRITABLE) && pipe(pw) == -1)) { - mrb_sys_fail(mrb, "pipe_open failed."); - return mrb_nil_value(); + if ((flags & FMODE_READABLE) && pipe(pr) == -1) { + mrb_sys_fail(mrb, "pipe"); + } + if ((flags & FMODE_WRITABLE) && pipe(pw) == -1) { + if (pr[0] != -1) close(pr[0]); + if (pr[1] != -1) close(pr[1]); + mrb_sys_fail(mrb, "pipe"); } if (!doexec) { -- cgit v1.2.3 From adb3bd669831ed1c04708076072313a209e2d848 Mon Sep 17 00:00:00 2001 From: Tomoyuki Sahara Date: Mon, 30 Sep 2013 10:55:09 +0900 Subject: don't retry when we cannot fork(2). EAGAIN indicates the system is under heavy load. Retrying make things worse. --- src/io.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/io.c b/src/io.c index 284d90877..d59d95b97 100644 --- a/src/io.c +++ b/src/io.c @@ -198,6 +198,7 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass) int pr[2] = { -1, -1 }; int pw[2] = { -1, -1 }; int doexec; + int saved_errno; mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt); io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type)); @@ -223,7 +224,6 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass) fflush(stderr); } -retry: switch (pid = fork()) { case 0: /* child */ if (flags & FMODE_READABLE) { @@ -250,24 +250,6 @@ retry: _exit(127); } return mrb_nil_value(); - case -1: /* error */ - if (errno == EAGAIN) { - goto retry; - } else { - int e = errno; - if (flags & FMODE_READABLE) { - close(pr[0]); - close(pr[1]); - } - if (flags & FMODE_WRITABLE) { - close(pw[0]); - close(pw[1]); - } - - errno = e; - mrb_sys_fail(mrb, "pipe_open failed."); - } - break; default: /* parent */ if (pid < 0) { mrb_sys_fail(mrb, "pipe_open failed."); @@ -298,6 +280,19 @@ retry: DATA_PTR(io) = fptr; return io; } + case -1: /* error */ + saved_errno = errno; + if (flags & FMODE_READABLE) { + close(pr[0]); + close(pr[1]); + } + if (flags & FMODE_WRITABLE) { + close(pw[0]); + close(pw[1]); + } + errno = saved_errno; + mrb_sys_fail(mrb, "pipe_open failed."); + break; } return mrb_nil_value(); -- cgit v1.2.3 From 97899a6922a016d4e488f929f009db78d46d7b1d Mon Sep 17 00:00:00 2001 From: Tomoyuki Sahara Date: Mon, 30 Sep 2013 11:18:22 +0900 Subject: style --- src/io.c | 64 +++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/src/io.c b/src/io.c index d59d95b97..e156733dd 100644 --- a/src/io.c +++ b/src/io.c @@ -162,9 +162,9 @@ mrb_io_alloc(mrb_state *mrb) struct mrb_io *fptr; fptr = (struct mrb_io *)mrb_malloc(mrb, sizeof(struct mrb_io)); - fptr->fd = -1; - fptr->fd2 = -1; - fptr->pid = 0; + fptr->fd = -1; + fptr->fd2 = -1; + fptr->pid = 0; return fptr; } @@ -188,7 +188,7 @@ io_open(mrb_state *mrb, mrb_value path, int flags, int perm) mrb_value mrb_io_s_popen(mrb_state *mrb, mrb_value klass) { - mrb_value cmd, io; + mrb_value cmd, io, result; mrb_value mode = mrb_str_new_cstr(mrb, "r"); mrb_value opt = mrb_hash_new(mrb); @@ -224,6 +224,7 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass) fflush(stderr); } + result = mrb_nil_value(); switch (pid = fork()) { case 0: /* child */ if (flags & FMODE_READABLE) { @@ -240,7 +241,6 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass) close(pw[0]); } } - if (doexec) { for (fd = 3; fd < NOFILE; fd++) { close(fd); @@ -249,37 +249,36 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass) mrb_raisef(mrb, E_IO_ERROR, "command not found: %s", pname); _exit(127); } - return mrb_nil_value(); + result = mrb_nil_value(); + break; + default: /* parent */ - if (pid < 0) { - mrb_sys_fail(mrb, "pipe_open failed."); - return mrb_nil_value(); + if ((flags & FMODE_READABLE) && (flags & FMODE_WRITABLE)) { + close(pr[1]); + fd = pr[0]; + close(pw[0]); + write_fd = pw[1]; + } else if (flags & FMODE_READABLE) { + close(pr[1]); + fd = pr[0]; } else { - if ((flags & FMODE_READABLE) && (flags & FMODE_WRITABLE)) { - close(pr[1]); - fd = pr[0]; - close(pw[0]); - write_fd = pw[1]; - } else if (flags & FMODE_READABLE) { - close(pr[1]); - fd = pr[0]; - } else { - close(pw[0]); - fd = pw[1]; - } + close(pw[0]); + fd = pw[1]; + } - mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, "")); - mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@pos"), mrb_fixnum_value(0)); + mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, "")); + mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@pos"), mrb_fixnum_value(0)); - fptr = mrb_io_alloc(mrb); - fptr->fd = fd; - fptr->fd2 = write_fd; - fptr->pid = pid; + fptr = mrb_io_alloc(mrb); + fptr->fd = fd; + fptr->fd2 = write_fd; + fptr->pid = pid; + + DATA_TYPE(io) = &mrb_io_type; + DATA_PTR(io) = fptr; + result = io; + break; - DATA_TYPE(io) = &mrb_io_type; - DATA_PTR(io) = fptr; - return io; - } case -1: /* error */ saved_errno = errno; if (flags & FMODE_READABLE) { @@ -294,8 +293,7 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass) mrb_sys_fail(mrb, "pipe_open failed."); break; } - - return mrb_nil_value(); + return result; } mrb_value -- cgit v1.2.3 From 8b48e2b4a42c9e1c697f94d3bb362a12484fe2e7 Mon Sep 17 00:00:00 2001 From: Tomoyuki Sahara Date: Mon, 30 Sep 2013 11:56:15 +0900 Subject: syswrite must write to fd2 if it is properly set. closes #3. --- src/io.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/io.c b/src/io.c index e156733dd..b80ff63b8 100644 --- a/src/io.c +++ b/src/io.c @@ -470,7 +470,7 @@ mrb_io_syswrite(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr; mrb_value str, buf; - int length; + int fd, length; mrb_get_args(mrb, "S", &str); if (mrb_type(str) != MRB_TT_STRING) { @@ -480,7 +480,12 @@ mrb_io_syswrite(mrb_state *mrb, mrb_value io) } fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type); - length = write(fptr->fd, RSTRING_PTR(buf), RSTRING_LEN(buf)); + if (fptr->fd2 == -1) { + fd = fptr->fd; + } else { + fd = fptr->fd2; + } + length = write(fd, RSTRING_PTR(buf), RSTRING_LEN(buf)); return mrb_fixnum_value(length); } -- cgit v1.2.3