ext/io/epoll/epoll.c in io-epoll-0.0.2 vs ext/io/epoll/epoll.c in io-epoll-0.1.0

- old
+ new

@@ -2,22 +2,29 @@ #include "ruby/io.h" #include "ruby/thread.h" #include <sys/epoll.h> VALUE cIO_Epoll; +VALUE cIO_Epoll_Event; struct Epoll { int epfd; int ev_len; }; static void +epoll_fd_close(int epfd) +{ + rb_thread_fd_close(epfd); +} + +static void rb_epoll_free(void *p) { struct Epoll *ptr = p; if (ptr) { - if (0 <= ptr->epfd) close(ptr->epfd); + if (0 <= ptr->epfd) epoll_fd_close(ptr->epfd); ruby_xfree(ptr); } } static size_t @@ -36,19 +43,34 @@ rb_epoll_memsize, }, NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED }; +void +epoll_check_initialized(struct Epoll *ptr) +{ + if (!ptr) { + rb_raise(rb_eIOError, "uninitialized stream"); + } +} + +void +epoll_check_closed(struct Epoll *ptr) +{ + epoll_check_initialized(ptr); + if (ptr->epfd < 0) { + rb_raise(rb_eIOError, "closed stream"); + } +} + static struct Epoll* get_epoll(VALUE self) { struct Epoll *ptr; rb_check_frozen(self); TypedData_Get_Struct(self, struct Epoll, &epoll_data_type, ptr); - if (!ptr) { - rb_raise(rb_eIOError, "uninitialized stream"); - } + epoll_check_initialized(ptr); return ptr; } static VALUE rb_epoll_allocate(VALUE klass) @@ -62,27 +84,52 @@ { struct Epoll *ptr; int epfd; TypedData_Get_Struct(self, struct Epoll, &epoll_data_type, ptr); - if (ptr->epfd < 0) close(ptr->epfd); + if (ptr->epfd < 0) epoll_fd_close(ptr->epfd); epfd = epoll_create(1); if (epfd == -1) { rb_sys_fail("epoll_create was failed"); } ptr->epfd = epfd; ptr->ev_len = 0; + + /** + * FIXME: I want to delete instance variable `evlist` ! + * It's just only using for GC mark. + * So, I don't know how to GC guard io objects. + */ + rb_ivar_set(self, rb_intern("evlist"), rb_ary_new()); + return self; } static VALUE rb_epoll_fileno(VALUE self) { struct Epoll *ptr = get_epoll(self); + epoll_check_closed(ptr); return INT2FIX(ptr->epfd); } +inline static void +rb_epoll_evlist_add(VALUE self, VALUE io) +{ + VALUE evlist = rb_ivar_get(self, rb_intern("evlist")); + rb_ary_push(evlist, io); + rb_ivar_set(self, rb_intern("evlist"), evlist); +} + +inline static void +rb_epoll_evlist_del(VALUE self, VALUE io) +{ + VALUE evlist = rb_ivar_get(self, rb_intern("evlist")); + rb_ary_delete(evlist, io); + rb_ivar_set(self, rb_intern("evlist"), evlist); +} + static VALUE rb_epoll_ctl(int argc, VALUE *argv, VALUE self) { struct Epoll *ptr = get_epoll(self); struct epoll_event ev; @@ -95,15 +142,25 @@ switch (rb_scan_args(argc, argv, "21", &flag, &io, &events)) { case 2: if (FIX2INT(flag) != EPOLL_CTL_DEL) rb_raise(rb_eArgError, "too few argument for CTL_ADD or CTL_MOD"); break; + rb_epoll_evlist_del(self, io); case 3: - if (FIX2INT(flag) != EPOLL_CTL_ADD && FIX2INT(flag) != EPOLL_CTL_MOD) + if (FIX2INT(flag) == EPOLL_CTL_ADD) { + rb_epoll_evlist_add(self, io); + } + else if (FIX2INT(flag) == EPOLL_CTL_MOD) { + /* nothing */ + } + else { rb_raise(rb_eArgError, "too many argument for CTL_DEL"); + } + if ((FIX2LONG(events) & (EPOLLIN|EPOLLPRI|EPOLLRDHUP|EPOLLOUT|EPOLLET|EPOLLONESHOT)) == 0) rb_raise(rb_eIOError, "undefined events"); + ev.events = FIX2LONG(events); ev.data.ptr = (void*)io; break; } @@ -148,19 +205,19 @@ static VALUE rb_epoll_wait(int argc, VALUE *argv, VALUE self) { struct Epoll *ptr = get_epoll(self); VALUE ready_evlist; - VALUE cEvent; VALUE event; struct epoll_event *evlist; int i, ready; int timeout = -1; struct epoll_wait_args data; if (argc == 1) timeout = FIX2INT(argv[0]); + if (ptr->ev_len <= 0) rb_raise(rb_eIOError, "empty interest list"); evlist = ruby_xmalloc(ptr->ev_len * sizeof(struct epoll_event)); @@ -179,13 +236,12 @@ rb_sys_fail("epoll_wait() was failed"); } } ready_evlist = rb_ary_new_capa(ready); - cEvent = rb_path2class("IO::Epoll::Event"); for (i = 0; i < ready; i++) { - event = rb_obj_alloc(cEvent); + event = rb_obj_alloc(cIO_Epoll_Event); RSTRUCT_SET(event, 0, (VALUE) evlist[i].data.ptr); RSTRUCT_SET(event, 1, LONG2FIX(evlist[i].events)); rb_ary_store(ready_evlist, i, event); } ruby_xfree(evlist); @@ -194,34 +250,42 @@ static VALUE rb_epoll_close(VALUE self) { struct Epoll *ptr = get_epoll(self); - if (close(ptr->epfd) == -1) { - rb_raise(rb_eIOError, "file descriptor duplicate close %ld", INT2FIX(ptr->epfd)); - } + epoll_check_closed(ptr); + epoll_fd_close(ptr->epfd); ptr->epfd = -1; return Qnil; } static VALUE +rb_epoll_closed_p(VALUE self) +{ + struct Epoll *ptr = get_epoll(self); + return 0 <= ptr->epfd ? Qfalse : Qtrue; +} + +static VALUE rb_epoll_length(VALUE self) { struct Epoll *ptr = get_epoll(self); return INT2FIX(ptr->ev_len); } void Init_epoll() { cIO_Epoll = rb_define_class_under(rb_cIO, "Epoll", rb_cObject); + cIO_Epoll_Event = rb_struct_define_under(cIO_Epoll, "Event", "data", "events", NULL); rb_define_alloc_func(cIO_Epoll, rb_epoll_allocate); rb_define_method(cIO_Epoll, "initialize", rb_epoll_initialize, 0); - rb_define_method(cIO_Epoll, "fileno", rb_epoll_fileno, 0); rb_define_method(cIO_Epoll, "ctl", rb_epoll_ctl, -1); rb_define_method(cIO_Epoll, "wait", rb_epoll_wait, -1); + rb_define_method(cIO_Epoll, "fileno", rb_epoll_fileno, 0); rb_define_method(cIO_Epoll, "close", rb_epoll_close, 0); + rb_define_method(cIO_Epoll, "closed?", rb_epoll_closed_p, 0); rb_define_method(cIO_Epoll, "length", rb_epoll_length, 0); rb_define_alias(cIO_Epoll, "size", "length"); rb_define_const(cIO_Epoll, "IN", INT2FIX(EPOLLIN)); rb_define_const(cIO_Epoll, "PRI", INT2FIX(EPOLLPRI)); rb_define_const(cIO_Epoll, "RDHUP", INT2FIX(EPOLLRDHUP));