Tizen 2.1 base
[platform/upstream/gcd.git] / kqueue-1.0.4 / src / linux / timer.c
1 /*
2  * Copyright (c) 2009 Mark Heily <mark@heily.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <pthread.h>
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <sys/epoll.h>
24 #include <sys/queue.h>
25 #include <sys/socket.h>
26 #include <sys/types.h>
27 #include <string.h>
28 #include <time.h>
29 #include <unistd.h>
30
31 /* Linux equivalents to kqueue(2) */
32 #include <sys/timerfd.h>
33
34 #include "sys/event.h"
35 #include "private.h"
36
37 #ifndef NDEBUG
38 static char *
39 itimerspec_dump(struct itimerspec *ts)
40 {
41     static char __thread buf[1024];
42
43     snprintf(buf, sizeof(buf),
44             "itimer: [ interval=%lu s %lu ns, next expire=%lu s %lu ns ]",
45             ts->it_interval.tv_sec,
46             ts->it_interval.tv_nsec,
47             ts->it_value.tv_sec,
48             ts->it_value.tv_nsec
49            );
50
51     return (buf);
52 }
53 #endif
54
55 /* Convert milliseconds into seconds+nanoseconds */
56 static void
57 convert_msec_to_itimerspec(struct itimerspec *dst, int src, int oneshot)
58 {
59     time_t sec, nsec;
60
61     sec = src / 1000;
62     nsec = (src % 1000) * 1000000;
63
64     /* Set the interval */
65     if (oneshot) {
66         dst->it_interval.tv_sec = 0;
67         dst->it_interval.tv_nsec = 0;
68     } else {
69         dst->it_interval.tv_sec = sec;
70         dst->it_interval.tv_nsec = nsec;
71     }
72
73     /* Set the initial expiration */
74     dst->it_value.tv_sec = sec;
75     dst->it_value.tv_nsec = nsec;
76     dbg_printf("%s", itimerspec_dump(dst));
77 }
78
79 static int
80 ktimer_delete(struct filter *filt, struct knote *kn)
81 {
82     int rv = 0;
83
84     if (kn->data.pfd == -1)
85         return (0);
86
87     dbg_printf("removing timerfd %d from %d", kn->data.pfd, filt->kf_pfd);
88     if (epoll_ctl(filt->kf_pfd, EPOLL_CTL_DEL, kn->data.pfd, NULL) < 0) {
89         dbg_printf("epoll_ctl(2): %s", strerror(errno));
90         rv = -1;
91     }
92     if (close(kn->data.pfd) < 0) {
93         dbg_printf("close(2): %s", strerror(errno));
94         rv = -1;
95     }
96
97     kn->data.pfd = -1;
98     return (rv);
99 }
100
101 int
102 evfilt_timer_init(struct filter *filt)
103 {
104     filt->kf_pfd = epoll_create(1);
105     if (filt->kf_pfd < 0)
106         return (-1);
107
108     dbg_printf("timer epollfd = %d", filt->kf_pfd);
109     return (0);
110 }
111
112 void
113 evfilt_timer_destroy(struct filter *filt)
114 {
115     close (filt->kf_pfd);//LAME
116 }
117
118 /* TODO: This entire function is copy+pasted from socket.c
119    with minor changes for timerfds.
120    Perhaps it could be refactored into a generic epoll_copyout()
121    that calls custom per-filter actions.
122    */
123 int
124 evfilt_timer_copyout(struct filter *filt, 
125             struct kevent *dst, 
126             int nevents)
127 {
128     struct epoll_event epevt[MAX_KEVENT];
129     struct epoll_event *ev;
130     struct knote *kn;
131     uint64_t expired;
132     int i, nret;
133     ssize_t n;
134
135     for (;;) {
136         nret = epoll_wait(filt->kf_pfd, &epevt[0], nevents, 0);
137         if (nret < 0) {
138             if (errno == EINTR)
139                 continue;
140             dbg_perror("epoll_wait");
141             return (-1);
142         } else {
143             break;
144         }
145     }
146
147     for (i = 0, nevents = 0; i < nret; i++) {
148         ev = &epevt[i];
149         /* TODO: put in generic debug.c: epoll_event_dump(ev); */
150         kn = ev->data.ptr;
151         memcpy(dst, &kn->kev, sizeof(*dst));
152         if (ev->events & EPOLLERR)
153             dst->fflags = 1; /* FIXME: Return the actual timer error */
154           
155         /* On return, data contains the number of times the
156            timer has been trigered.
157              */
158         n = read(kn->data.pfd, &expired, sizeof(expired));
159         if (n < 0 || n < sizeof(expired)) {
160             dbg_puts("invalid read from timerfd");
161             expired = 1;  /* Fail gracefully */
162         } 
163         dst->data = expired;
164
165         if (kn->kev.flags & EV_DISPATCH) {
166             KNOTE_DISABLE(kn);
167             ktimer_delete(filt, kn);
168         } else if (kn->kev.flags & EV_ONESHOT) {
169             ktimer_delete(filt, kn);
170             knote_free(filt, kn);
171         }
172
173         nevents++;
174         dst++;
175     }
176
177     return (nevents);
178 }
179
180 int
181 evfilt_timer_knote_create(struct filter *filt, struct knote *kn)
182 {
183     struct epoll_event ev;
184     struct itimerspec ts;
185     int tfd;
186
187     kn->kev.flags |= EV_CLEAR;
188
189     tfd = timerfd_create(CLOCK_MONOTONIC, 0);
190     if (tfd < 0) {
191         dbg_printf("timerfd_create(2): %s", strerror(errno));
192         return (-1);
193     }
194     dbg_printf("created timerfd %d", tfd);
195
196     convert_msec_to_itimerspec(&ts, kn->kev.data, kn->kev.flags & EV_ONESHOT);
197     if (timerfd_settime(tfd, 0, &ts, NULL) < 0) {
198         dbg_printf("timerfd_settime(2): %s", strerror(errno));
199         close(tfd);
200         return (-1);
201     }
202
203     memset(&ev, 0, sizeof(ev));
204     ev.events = EPOLLIN;
205     ev.data.ptr = kn;
206     if (epoll_ctl(filt->kf_pfd, EPOLL_CTL_ADD, tfd, &ev) < 0) {
207         dbg_printf("epoll_ctl(2): %d", errno);
208         close(tfd);
209         return (-1);
210     }
211
212     kn->data.pfd = tfd;
213     return (0);
214 }
215
216 int
217 evfilt_timer_knote_modify(struct filter *filt, struct knote *kn, 
218         const struct kevent *kev)
219 {
220     return (0); /* STUB */
221 }
222
223 int
224 evfilt_timer_knote_delete(struct filter *filt, struct knote *kn)
225 {
226     return (ktimer_delete(filt,kn));
227 }
228
229 int
230 evfilt_timer_knote_enable(struct filter *filt, struct knote *kn)
231 {
232     return evfilt_timer_knote_create(filt, kn);
233 }
234
235 int
236 evfilt_timer_knote_disable(struct filter *filt, struct knote *kn)
237 {
238     return evfilt_timer_knote_delete(filt, kn);
239 }
240
241 const struct filter evfilt_timer = {
242     EVFILT_TIMER,
243     evfilt_timer_init,
244     evfilt_timer_destroy,
245     evfilt_timer_copyout,
246     evfilt_timer_knote_create,
247     evfilt_timer_knote_modify,
248     evfilt_timer_knote_delete,
249     evfilt_timer_knote_enable,
250     evfilt_timer_knote_disable,     
251 };