epollexclusive: use maxevents=1 for epoll_wait
[unicorn.git] / ext / unicorn_http / epollexclusive.h
blob8f4ea9a03763ae3373ee7de83735d8d2de907456
1 /*
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>
13 # include <errno.h>
14 # include <ruby/io.h>
15 # include <ruby/thread.h>
16 #endif /* __linux__ */
18 #if defined(EPOLLEXCLUSIVE) && defined(HAVE_EPOLL_CREATE1)
19 # define USE_EPOLL (1)
20 #else
21 # define USE_EPOLL (0)
22 #endif
24 #if USE_EPOLL
26 * :nodoc:
27 * returns IO object if EPOLLEXCLUSIVE works and arms readers
29 static VALUE prep_readers(VALUE cls, VALUE readers)
31 long i;
32 int epfd = epoll_create1(EPOLL_CLOEXEC);
33 VALUE epio;
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++) {
41 int rc;
42 struct epoll_event e;
43 rb_io_t *fptr;
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");
61 return epio;
63 #endif /* USE_EPOLL */
65 #if USE_EPOLL
66 struct ep_wait {
67 struct epoll_event event;
68 rb_io_t *fptr;
69 int timeout_msec;
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,
82 epw->timeout_msec);
85 /* :nodoc: */
86 /* readers must not change between prepare_readers and get_readers */
87 static VALUE
88 get_readers(VALUE epio, VALUE ready, VALUE readers, VALUE timeout_msec)
90 struct ep_wait epw;
91 long n;
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);
100 if (n < 0) {
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);
105 if (RTEST(obj))
106 rb_ary_push(ready, obj);
107 } /* n == 0 : timeout */
108 return Qfalse;
110 #endif /* USE_EPOLL */
112 static void init_epollexclusive(VALUE mUnicorn)
114 #if USE_EPOLL
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);
118 #endif