2 * This is only intended for use inside a unicorn worker, nowhere else.
3 * EPOLLEXCLUSIVE somewhat mitigates the thundering herd problem for
4 * mostly idle processes since we can't use blocking accept4.
5 * This is NOT intended for use with multi-threaded servers, nor
6 * single-threaded multi-client ("C10K") servers or anything advanced
7 * like that. This use of epoll is only appropriate for a primitive,
8 * single-client, single-threaded servers like unicorn that need to
9 * support SIGKILL timeouts and parent death detection.
11 #if defined(HAVE_EPOLL_CREATE1)
12 # include <sys/epoll.h>
15 # include <ruby/thread.h>
16 #endif /* __linux__ */
18 #if defined(EPOLLEXCLUSIVE) && defined(HAVE_EPOLL_CREATE1)
19 # define USE_EPOLL (1)
21 # define USE_EPOLL (0)
27 * returns IO object if EPOLLEXCLUSIVE works and arms readers
29 static VALUE
prep_readers(VALUE cls
, VALUE readers
)
32 int epfd
= epoll_create1(EPOLL_CLOEXEC
);
35 if (epfd
< 0) rb_sys_fail("epoll_create1");
37 epio
= rb_funcall(cls
, rb_intern("for_fd"), 1, INT2NUM(epfd
));
39 Check_Type(readers
, T_ARRAY
);
40 for (i
= 0; i
< RARRAY_LEN(readers
); i
++) {
44 VALUE io
= rb_ary_entry(readers
, i
);
46 e
.data
.u64
= i
; /* the reason readers shouldn't change */
49 * I wanted to use EPOLLET here, but maintaining our own
50 * equivalent of ep->rdllist in Ruby-space doesn't fit
51 * our design at all (and the kernel already has it's own
52 * code path for doing it). So let the kernel spend
53 * cycles on maintaining level-triggering.
55 e
.events
= EPOLLEXCLUSIVE
| EPOLLIN
;
56 io
= rb_io_get_io(io
);
57 GetOpenFile(io
, fptr
);
58 rc
= epoll_ctl(epfd
, EPOLL_CTL_ADD
, fptr
->fd
, &e
);
59 if (rc
< 0) rb_sys_fail("epoll_ctl");
63 #endif /* USE_EPOLL */
67 struct epoll_event event
;
72 static void *do_wait(void *ptr
) /* runs w/o GVL */
74 struct ep_wait
*epw
= ptr
;
76 * Linux delivers epoll events in the order received, and using
77 * maxevents=1 ensures we pluck one item off ep->rdllist
78 * at-a-time (c.f. fs/eventpoll.c in linux.git, it's quite
79 * easy-to-understand for anybody familiar with Ruby C).
81 return (void *)(long)epoll_wait(epw
->fptr
->fd
, &epw
->event
, 1,
86 /* readers must not change between prepare_readers and get_readers */
88 get_readers(VALUE epio
, VALUE ready
, VALUE readers
, VALUE timeout_msec
)
93 Check_Type(ready
, T_ARRAY
);
94 Check_Type(readers
, T_ARRAY
);
95 epio
= rb_io_get_io(epio
);
96 GetOpenFile(epio
, epw
.fptr
);
98 epw
.timeout_msec
= NUM2INT(timeout_msec
);
99 n
= (long)rb_thread_call_without_gvl(do_wait
, &epw
, RUBY_UBF_IO
, NULL
);
101 if (errno
!= EINTR
) rb_sys_fail("epoll_wait");
102 } else if (n
> 0) { /* maxevents is hardcoded to 1 */
103 VALUE obj
= rb_ary_entry(readers
, epw
.event
.data
.u64
);
106 rb_ary_push(ready
, obj
);
107 } /* n == 0 : timeout */
110 #endif /* USE_EPOLL */
112 static void init_epollexclusive(VALUE mUnicorn
)
115 VALUE cWaiter
= rb_define_class_under(mUnicorn
, "Waiter", rb_cIO
);
116 rb_define_singleton_method(cWaiter
, "prep_readers", prep_readers
, 1);
117 rb_define_method(cWaiter
, "get_readers", get_readers
, 3);