root/src/eventer/eventer_epoll_impl.c

Revision 8eea7aa3f6e6ab3f369279099deafddbdee847e7, 8.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 months ago)

Avoid modifications on descheduled fds and the coupled assertion.

If the callback removes the event, but does not return 0 it is a bug,
but not one so heinous as to solicit an assertion. This only means
that deep in the callback someone did something to deschedule this
event and didn't communicate that all the back up the stack so the
immediate caller returned some interest.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34 #include "eventer/eventer.h"
35 #include "utils/noit_atomic.h"
36 #include "utils/noit_skiplist.h"
37 #include "utils/noit_log.h"
38 #include "dtrace_probes.h"
39
40 #include <errno.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <sys/epoll.h>
44 #include <signal.h>
45 #include <pthread.h>
46 #include <assert.h>
47
48 struct _eventer_impl eventer_epoll_impl;
49 #define LOCAL_EVENTER eventer_epoll_impl
50 #define LOCAL_EVENTER_foreach_fdevent eventer_epoll_impl_foreach_fdevent
51 #define maxfds LOCAL_EVENTER.maxfds
52 #define master_fds LOCAL_EVENTER.master_fds
53
54 #include "eventer/eventer_impl_private.h"
55
56 static int *masks;
57 static int epoll_fd = -1;
58
59 static int eventer_epoll_impl_init() {
60   struct rlimit rlim;
61   int rv;
62
63   /* super init */
64   if((rv = eventer_impl_init()) != 0) return rv;
65
66   signal(SIGPIPE, SIG_IGN);
67   epoll_fd = epoll_create(1024);
68   if(epoll_fd == -1) {
69     return -1;
70   }
71   getrlimit(RLIMIT_NOFILE, &rlim);
72   maxfds = rlim.rlim_cur;
73   master_fds = calloc(maxfds, sizeof(*master_fds));
74   masks = calloc(maxfds, sizeof(*masks));
75   return 0;
76 }
77 static int eventer_epoll_impl_propset(const char *key, const char *value) {
78   if(eventer_impl_propset(key, value)) {
79     /* Do our epoll local properties here */
80     return -1;
81   }
82   return 0;
83 }
84 static void eventer_epoll_impl_add(eventer_t e) {
85   struct epoll_event _ev;
86   ev_lock_state_t lockstate;
87   assert(e->mask);
88
89   if(e->mask & EVENTER_ASYNCH) {
90     eventer_add_asynch(NULL, e);
91     return;
92   }
93
94   /* Recurrent delegation */
95   if(e->mask & EVENTER_RECURRENT) {
96     eventer_add_recurrent(e);
97     return;
98   }
99
100   /* Timed events are simple */
101   if(e->mask & EVENTER_TIMER) {
102     eventer_add_timed(e);
103     return;
104   }
105
106   /* file descriptor event */
107   assert(e->whence.tv_sec == 0 && e->whence.tv_usec == 0);
108   memset(&_ev, 0, sizeof(_ev));
109   _ev.data.fd = e->fd;
110   if(e->mask & EVENTER_READ) _ev.events |= (EPOLLIN|EPOLLPRI);
111   if(e->mask & EVENTER_WRITE) _ev.events |= (EPOLLOUT);
112   if(e->mask & EVENTER_EXCEPTION) _ev.events |= (EPOLLERR|EPOLLHUP);
113
114   lockstate = acquire_master_fd(e->fd);
115   master_fds[e->fd].e = e;
116
117   assert(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, e->fd, &_ev) == 0);
118
119   release_master_fd(e->fd, lockstate);
120 }
121 static eventer_t eventer_epoll_impl_remove(eventer_t e) {
122   eventer_t removed = NULL;
123   if(e->mask & EVENTER_ASYNCH) {
124     abort();
125   }
126   if(e->mask & (EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION)) {
127     ev_lock_state_t lockstate;
128     struct epoll_event _ev;
129     memset(&_ev, 0, sizeof(_ev));
130     _ev.data.fd = e->fd;
131     lockstate = acquire_master_fd(e->fd);
132     if(e == master_fds[e->fd].e) {
133       removed = e;
134       master_fds[e->fd].e = NULL;
135       assert(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, e->fd, &_ev) == 0);
136     }
137     release_master_fd(e->fd, lockstate);
138   }
139   else if(e->mask & EVENTER_TIMER) {
140     removed = eventer_remove_timed(e);
141   }
142   else if(e->mask & EVENTER_RECURRENT) {
143     removed = eventer_remove_recurrent(e);
144   }
145   else {
146     abort();
147   }
148   return removed;
149 }
150 static void eventer_epoll_impl_update(eventer_t e, int mask) {
151   struct epoll_event _ev;
152   if(e->mask & EVENTER_TIMER) {
153     eventer_update_timed(e,mask);
154     return;
155   }
156   memset(&_ev, 0, sizeof(_ev));
157   _ev.data.fd = e->fd;
158   e->mask = mask;
159   if(e->mask & (EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION)) {
160     if(e->mask & EVENTER_READ) _ev.events |= (EPOLLIN|EPOLLPRI);
161     if(e->mask & EVENTER_WRITE) _ev.events |= (EPOLLOUT);
162     if(e->mask & EVENTER_EXCEPTION) _ev.events |= (EPOLLERR|EPOLLHUP);
163     assert(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, e->fd, &_ev) == 0);
164   }
165 }
166 static eventer_t eventer_epoll_impl_remove_fd(int fd) {
167   eventer_t eiq = NULL;
168   ev_lock_state_t lockstate;
169   if(master_fds[fd].e) {
170     struct epoll_event _ev;
171     memset(&_ev, 0, sizeof(_ev));
172     _ev.data.fd = fd;
173     lockstate = acquire_master_fd(fd);
174     eiq = master_fds[fd].e;
175     master_fds[fd].e = NULL;
176     assert(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &_ev) == 0);
177     release_master_fd(fd, lockstate);
178   }
179   return eiq;
180 }
181 static eventer_t eventer_epoll_impl_find_fd(int fd) {
182   return master_fds[fd].e;
183 }
184
185 static void eventer_epoll_impl_trigger(eventer_t e, int mask) {
186   struct timeval __now;
187   int fd, newmask;
188   const char *cbname;
189   ev_lock_state_t lockstate;
190
191   fd = e->fd;
192   if(e != master_fds[fd].e) return;
193   lockstate = acquire_master_fd(fd);
194   if(lockstate == EV_ALREADY_OWNED) return;
195   assert(lockstate == EV_OWNED);
196
197   gettimeofday(&__now, NULL);
198   cbname = eventer_name_for_callback(e->callback);
199   noitLT(eventer_deb, &__now, "epoll: fire on %d/%x to %s(%p)\n",
200          fd, mask, cbname?cbname:"???", e->callback);
201   EVENTER_CALLBACK_ENTRY((void *)e->callback, (char *)cbname, fd, e->mask, mask);
202   newmask = e->callback(e, mask, e->closure, &__now);
203   EVENTER_CALLBACK_RETURN((void *)e->callback, (char *)cbname, newmask);
204
205   if(newmask) {
206     struct epoll_event _ev;
207     memset(&_ev, 0, sizeof(_ev));
208     _ev.data.fd = fd;
209     if(newmask & EVENTER_READ) _ev.events |= (EPOLLIN|EPOLLPRI);
210     if(newmask & EVENTER_WRITE) _ev.events |= (EPOLLOUT);
211     if(newmask & EVENTER_EXCEPTION) _ev.events |= (EPOLLERR|EPOLLHUP);
212     if(master_fds[fd].e == NULL) {
213       noitL(noit_error, "eventer %s(%p) epoll asked to modify descheduled fd: %d\n",
214             cbname?cbname:"???", e->callback, fd);
215     } else {
216       assert(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &_ev) == 0);
217     }
218     /* Set our mask */
219     e->mask = newmask;
220   }
221   else {
222     /* see kqueue implementation for details on the next line */
223     if(master_fds[fd].e == e) master_fds[fd].e = NULL;
224     eventer_free(e);
225   }
226   release_master_fd(fd, lockstate);
227 }
228 static int eventer_epoll_impl_loop() {
229   struct epoll_event *epev;
230
231   epev = malloc(sizeof(*epev) * maxfds);
232
233   while(1) {
234     struct timeval __now, __sleeptime;
235     int fd_cnt = 0;
236
237     __sleeptime = eventer_max_sleeptime;
238
239     eventer_dispatch_timed(&__now, &__sleeptime);
240
241     /* Handle recurrent events */
242     eventer_dispatch_recurrent(&__now);
243
244     /* Now we move on to our fd-based events */
245     fd_cnt = epoll_wait(epoll_fd, epev, maxfds,
246                         __sleeptime.tv_sec * 1000 + __sleeptime.tv_usec / 1000);
247     noitLT(eventer_deb, &__now, "debug: epoll_wait(%d, [], %d) => %d\n", epoll_fd, maxfds, fd_cnt);
248     if(fd_cnt < 0) {
249       noitLT(eventer_err, &__now, "epoll_wait: %s\n", strerror(errno));
250     }
251     else {
252       int idx;
253       /* loop once to clear */
254       for(idx = 0; idx < fd_cnt; idx++) {
255         struct epoll_event *ev;
256         eventer_t e;
257         int fd, mask = 0;
258
259         ev = &epev[idx];
260
261         if(ev->events & (EPOLLIN | EPOLLPRI)) mask |= EVENTER_READ;
262         if(ev->events & (EPOLLOUT)) mask |= EVENTER_WRITE;
263         if(ev->events & (EPOLLERR|EPOLLHUP)) mask |= EVENTER_EXCEPTION;
264
265         fd = ev->data.fd;
266
267         e = master_fds[fd].e;
268         /* It's possible that someone removed the event and freed it
269          * before we got here.
270          */
271         if(!e) continue;
272
273         eventer_epoll_impl_trigger(e, mask);
274       }
275     }
276   }
277   /* NOTREACHED */
278   return 0;
279 }
280
281 struct _eventer_impl eventer_epoll_impl = {
282   "epoll",
283   eventer_epoll_impl_init,
284   eventer_epoll_impl_propset,
285   eventer_epoll_impl_add,
286   eventer_epoll_impl_remove,
287   eventer_epoll_impl_update,
288   eventer_epoll_impl_remove_fd,
289   eventer_epoll_impl_find_fd,
290   eventer_epoll_impl_trigger,
291   eventer_epoll_impl_loop,
292   eventer_epoll_impl_foreach_fdevent,
293   { 0, 200000 },
294   0,
295   NULL
296 };
Note: See TracBrowser for help on using the browser.