Blob


1 /*
2 * This is free and unencumbered software released into the public domain.
3 *
4 * Anyone is free to copy, modify, publish, use, compile, sell, or
5 * distribute this software, either in source code form or as a compiled
6 * binary, for any purpose, commercial or non-commercial, and by any
7 * means.
8 *
9 * In jurisdictions that recognize copyright laws, the author or authors
10 * of this software dedicate any and all copyright interest in the
11 * software to the public domain. We make this dedication for the benefit
12 * of the public at large and to the detriment of our heirs and
13 * successors. We intend this dedication to be an overt act of
14 * relinquishment in perpetuity of all present and future rights to this
15 * software under copyright law.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
26 #include "config.h"
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <limits.h>
31 #include <poll.h>
32 #include <signal.h>
33 #include <stdlib.h>
34 #include <time.h>
35 #include <unistd.h>
37 #include "ev.h"
39 struct evcb {
40 void (*cb)(int, int, void *);
41 void *udata;
42 };
44 struct evbase {
45 size_t len;
47 struct pollfd *pfds;
48 size_t pfdlen;
50 struct evcb *cbs;
51 size_t cblen;
53 int sigpipe[2];
54 struct evcb sigcb;
56 int timeout;
57 struct evcb toutcb;
58 };
60 static struct evbase *base;
61 static int ev_stop;
63 static int
64 ev_resize(size_t len)
65 {
66 void *t;
67 size_t i;
69 t = recallocarray(base->pfds, base->pfdlen, len, sizeof(*base->pfds));
70 if (t == NULL)
71 return -1;
72 base->pfds = t;
73 base->pfdlen = len;
75 for (i = base->len; i < len; ++i)
76 base->pfds[i].fd = -1;
78 t = recallocarray(base->cbs, base->cblen, len, sizeof(*base->cbs));
79 if (t == NULL)
80 return -1;
81 base->cbs = t;
82 base->cblen = len;
84 base->len = len;
85 return 0;
86 }
88 int
89 ev_init(void)
90 {
91 if (base != NULL) {
92 errno = EINVAL;
93 return -1;
94 }
96 if ((base = calloc(1, sizeof(*base))) == NULL)
97 return -1;
99 base->sigpipe[0] = -1;
100 base->sigpipe[1] = -1;
101 base->timeout = INFTIM;
103 if (ev_resize(16) == -1) {
104 free(base->pfds);
105 free(base->cbs);
106 free(base);
107 base = NULL;
108 return -1;
111 return 0;
114 int
115 ev_add(int fd, int ev, void (*cb)(int, int, void *), void *udata)
117 if (fd >= base->len) {
118 if (ev_resize(fd + 1) == -1)
119 return -1;
122 base->pfds[fd].fd = fd;
123 base->pfds[fd].events = ev;
125 base->cbs[fd].cb = cb;
126 base->cbs[fd].udata = udata;
128 return 0;
131 static void
132 ev_sigcatch(int signo)
134 unsigned char s;
135 int err;
137 err = errno;
139 /*
140 * We should be able to write up to PIPE_BUF bytes without
141 * blocking.
142 */
143 s = signo;
144 (void) write(base->sigpipe[1], &s, sizeof(s));
146 errno = err;
149 static void
150 ev_sigdispatch(int fd, int ev, void *data)
152 unsigned char signo;
154 if (read(fd, &signo, sizeof(signo)) != sizeof(signo))
155 return;
157 base->sigcb.cb(signo, 0, base->sigcb.udata);
160 int
161 ev_signal(int sig, void (*cb)(int, int, void *), void *udata)
163 int flags;
165 if (base->sigpipe[0] == -1) {
166 /* pipe2(2) is not available everywhere... sigh */
167 if (pipe(base->sigpipe) == -1)
168 return -1;
170 if ((flags = fcntl(base->sigpipe[1], F_GETFL)) == -1 ||
171 fcntl(base->sigpipe[1], F_SETFL, flags | O_NONBLOCK) == -1)
172 return -1;
174 if (ev_add(base->sigpipe[0], POLLIN, ev_sigdispatch, NULL)
175 == -1)
176 return -1;
179 base->sigcb.cb = cb;
180 base->sigcb.udata = udata;
182 signal(sig, ev_sigcatch);
183 return 0;
186 int
187 ev_timer(const struct timeval *tv, void (*cb)(int, int, void*), void *udata)
189 base->timeout = INFTIM;
190 if (tv) {
191 base->timeout = tv->tv_sec * 1000;
192 base->timeout += tv->tv_usec / 1000;
195 base->toutcb.cb = cb;
196 base->toutcb.udata = udata;
198 return 0;
201 int
202 ev_timer_pending(void)
204 return base->timeout != INFTIM;
207 int
208 ev_del(int fd)
210 if (fd >= base->len) {
211 errno = ERANGE;
212 return -1;
215 base->pfds[fd].fd = -1;
216 base->pfds[fd].events = 0;
218 base->cbs[fd].cb = NULL;
219 base->cbs[fd].udata = NULL;
221 return 0;
224 int
225 ev_loop(void)
227 struct timespec elapsed, beg, end;
228 int n, em;
229 size_t i;
231 while (!ev_stop) {
232 clock_gettime(CLOCK_MONOTONIC, &beg);
233 if ((n = poll(base->pfds, base->len, base->timeout)) == -1) {
234 if (errno != EINTR)
235 return -1;
237 clock_gettime(CLOCK_MONOTONIC, &end);
239 timespecsub(&end, &beg, &elapsed);
240 em = elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000;
241 if (base->timeout != INFTIM) {
242 if (base->timeout - em < 0 || n == 0) {
243 base->timeout = INFTIM;
244 base->toutcb.cb(-1, 0, base->toutcb.udata);
245 } else
246 base->timeout -= em;
249 for (i = 0; i < base->len && n > 0 && !ev_stop; ++i) {
250 if (base->pfds[i].fd == -1)
251 continue;
252 if (base->pfds[i].revents & (POLLIN|POLLOUT|POLLHUP)) {
253 n--;
254 base->cbs[i].cb(base->pfds[i].fd,
255 base->pfds[i].revents,
256 base->cbs[i].udata);
261 return 0;
264 void
265 ev_break(void)
267 ev_stop = 1;