ext/sleepy_penguin/inotify.c in sleepy_penguin-3.1.0 vs ext/sleepy_penguin/inotify.c in sleepy_penguin-3.1.0.26.g7181
- old
+ new
@@ -2,11 +2,16 @@
#include "sleepy_penguin.h"
#include <sys/inotify.h>
#include <sys/ioctl.h>
#include "missing_inotify.h"
-static ID id_inotify_buf, id_inotify_tmp, id_mask;
+struct inbuf {
+ size_t capa;
+ void *ptr;
+};
+
+static ID id_inotify_tmp, id_mask;
static VALUE cEvent, checks;
/*
* call-seq:
* Inotify.new([flags]) -> Inotify IO object
@@ -20,25 +25,24 @@
VALUE _flags, rv;
int flags;
int fd;
rb_scan_args(argc, argv, "01", &_flags);
- flags = rb_sp_get_flags(klass, _flags);
+ flags = rb_sp_get_flags(klass, _flags, RB_SP_CLOEXEC(IN_CLOEXEC));
fd = inotify_init1(flags);
- if (fd == -1) {
+ if (fd < 0) {
if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
rb_gc();
fd = inotify_init1(flags);
}
- if (fd == -1)
+ if (fd < 0)
rb_sys_fail("inotify_init1");
}
rv = INT2FIX(fd);
rv = rb_call_super(1, &rv);
- rb_ivar_set(rv, id_inotify_buf, rb_str_new(0, 128));
rb_ivar_set(rv, id_inotify_tmp, rb_ary_new());
return rv;
}
@@ -87,18 +91,13 @@
int fd = rb_sp_fileno(self);
const char *pathname = StringValueCStr(path);
uint32_t mask = rb_sp_get_uflags(self, vmask);
int rc = inotify_add_watch(fd, pathname, mask);
- if (rc == -1) {
- if (errno == ENOMEM) {
- rb_gc();
- rc = inotify_add_watch(fd, pathname, mask);
- }
- if (rc == -1)
- rb_sys_fail("inotify_add_watch");
- }
+ if (rc < 0)
+ rb_sys_fail("inotify_add_watch");
+
return UINT2NUM((uint32_t)rc);
}
/*
* call-seq:
@@ -111,11 +110,11 @@
{
uint32_t wd = NUM2UINT(vwd);
int fd = rb_sp_fileno(self);
int rc = inotify_rm_watch(fd, wd);
- if (rc == -1)
+ if (rc < 0)
rb_sys_fail("inotify_rm_watch");
return INT2NUM(rc);
}
static size_t event_len(struct inotify_event *e)
@@ -136,32 +135,68 @@
return rb_struct_new(cEvent, wd, mask, cookie, name);
}
struct inread_args {
int fd;
- struct inotify_event *ptr;
- long len;
+ struct inbuf *inbuf;
};
static VALUE inread(void *ptr)
{
struct inread_args *args = ptr;
- return (VALUE)read(args->fd, args->ptr, args->len);
+ return (VALUE)read(args->fd, args->inbuf->ptr, args->inbuf->capa);
}
+static void inbuf_grow(struct inbuf *inbuf, size_t size)
+{
+ int err;
+
+ if (inbuf->capa >= size)
+ return;
+ free(inbuf->ptr);
+ err = posix_memalign(&inbuf->ptr, rb_sp_l1_cache_line_size, size);
+ if (err) {
+ errno = err;
+ rb_memerror();
+ }
+ inbuf->capa = size;
+}
+
+static void resize_internal_buffer(struct inread_args *args)
+{
+ int newlen;
+
+ if (args->inbuf->capa > 0x10000)
+ rb_raise(rb_eRuntimeError, "path too long");
+
+ if (ioctl(args->fd, FIONREAD, &newlen) != 0)
+ rb_sys_fail("ioctl(inotify,FIONREAD)");
+
+ if (newlen > 0)
+ inbuf_grow(args->inbuf, (size_t)newlen);
+
+ if (newlen == 0) /* race: some other thread grabbed the data */
+ return;
+
+ rb_raise(rb_eRuntimeError,
+ "ioctl(inotify,FIONREAD) returned negative length: %d",
+ newlen);
+}
+
/*
* call-seq:
* ino.take([nonblock]) -> Inotify::Event or nil
*
* Returns the next Inotify::Event processed. May return +nil+ if +nonblock+
* is +true+.
*/
static VALUE take(int argc, VALUE *argv, VALUE self)
{
+ static __thread struct inbuf inbuf;
+
struct inread_args args;
- VALUE buf;
VALUE tmp = rb_ivar_get(self, id_inotify_tmp);
struct inotify_event *e, *end;
ssize_t r;
VALUE rv = Qnil;
VALUE nonblock;
@@ -169,46 +204,35 @@
if (RARRAY_LEN(tmp) > 0)
return rb_ary_shift(tmp);
rb_scan_args(argc, argv, "01", &nonblock);
+ inbuf_grow(&inbuf, 128);
args.fd = rb_sp_fileno(self);
- buf = rb_ivar_get(self, id_inotify_buf);
- args.len = RSTRING_LEN(buf);
- args.ptr = (struct inotify_event *)RSTRING_PTR(buf);
+ args.inbuf = &inbuf;
if (RTEST(nonblock))
rb_sp_set_nonblock(args.fd);
else
blocking_io_prepare(args.fd);
do {
- r = rb_sp_fd_region(inread, &args, args.fd);
+ r = (ssize_t)rb_sp_fd_region(inread, &args, args.fd);
if (r == 0 /* Linux < 2.6.21 */
||
(r < 0 && errno == EINVAL) /* Linux >= 2.6.21 */
) {
- /* resize internal buffer */
- int newlen;
- if (args.len > 0x10000)
- rb_raise(rb_eRuntimeError, "path too long");
- if (ioctl(args.fd, FIONREAD, &newlen) != 0)
- rb_sys_fail("ioctl(inotify,FIONREAD)");
- rb_str_resize(buf, newlen);
- args.ptr = (struct inotify_event *)RSTRING_PTR(buf);
- args.len = newlen;
+ resize_internal_buffer(&args);
} else if (r < 0) {
- if (errno == EAGAIN && RTEST(nonblock)) {
+ if (errno == EAGAIN && RTEST(nonblock))
return Qnil;
- } else {
- args.fd = rb_sp_fileno(self);
- if (!rb_io_wait_readable(args.fd))
- rb_sys_fail("read(inotify)");
- }
+ if (!rb_sp_wait(rb_io_wait_readable, self, &args.fd))
+ rb_sys_fail("read(inotify)");
} else {
/* buffer in userspace to minimize read() calls */
- end = (struct inotify_event *)((char *)args.ptr + r);
- for (e = args.ptr; e < end; ) {
+ end = (struct inotify_event *)
+ ((char *)args.inbuf->ptr + r);
+ for (e = args.inbuf->ptr; e < end; ) {
VALUE event = event_new(e);
if (NIL_P(rv))
rv = event;
else
rb_ary_push(tmp, event);
@@ -247,26 +271,10 @@
return rv;
}
/*
* call-seq:
- * inotify.dup -> another Inotify object
- *
- * Duplicates an Inotify object, allowing it to be used in a blocking
- * fashion in another thread. Ensures duplicated Inotify objects do
- * not share read buffers, but do share the userspace Array buffer.
- */
-static VALUE init_copy(VALUE dest, VALUE orig)
-{
- rb_call_super(1, &orig); /* copy all other ivars as-is */
- rb_ivar_set(dest, id_inotify_buf, rb_str_new(0, 128));
-
- return dest;
-}
-
-/*
- * call-seq:
* ino.each { |event| ... } -> ino
*
* Yields each Inotify::Event received in a blocking fashion.
*/
static VALUE each(VALUE self)
@@ -306,11 +314,10 @@
* end
*/
cInotify = rb_define_class_under(mSleepyPenguin, "Inotify", rb_cIO);
rb_define_method(cInotify, "add_watch", add_watch, 2);
rb_define_method(cInotify, "rm_watch", rm_watch, 1);
- rb_define_method(cInotify, "initialize_copy", init_copy, 1);
rb_define_method(cInotify, "take", take, -1);
rb_define_method(cInotify, "each", each, 0);
/*
* Document-class: SleepyPenguin::Inotify::Event
@@ -336,10 +343,9 @@
*/
cEvent = rb_struct_define("Event", "wd", "mask", "cookie", "name", 0);
cEvent = rb_define_class_under(cInotify, "Event", cEvent);
rb_define_method(cEvent, "events", events, 0);
rb_define_singleton_method(cInotify, "new", s_new, -1);
- id_inotify_buf = rb_intern("@inotify_buf");
id_inotify_tmp = rb_intern("@inotify_tmp");
id_mask = rb_intern("mask");
checks = rb_ary_new();
rb_global_variable(&checks);
#define IN(x) rb_define_const(cInotify,#x,UINT2NUM(IN_##x))