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))