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/queue.h>
24#include <sys/socket.h>
25#include <sys/types.h>
26#include <string.h>
27#include <time.h>
28#include <unistd.h>
29
30#include "sys/event.h"
31#include "private.h"
32
33/* A request to sleep for a certain time */
34struct sleepreq {
35    int         pfd;            /* fd to poll for ACKs */
36    int         wfd;            /* fd to wake up when sleep is over */
37    uintptr_t   ident;          /* from kevent */
38    intptr_t    interval;       /* sleep time, in milliseconds */
39    struct sleepstat *stat;
40};
41
42/* Information about a successful sleep operation */
43struct sleepinfo {
44    uintptr_t   ident;          /* from kevent */
45    uintptr_t   counter;        /* number of times the timer expired */
46};
47
48static void *
49sleeper_thread(void *arg)
50{
51    struct sleepreq sr;
52    struct sleepinfo si;
53    struct timespec req, rem;
54    sigset_t        mask;
55    ssize_t         cnt;
56    bool            cts = true;     /* Clear To Send */
57    char            buf[1];
58
59    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
60
61    /* Copyin the request */
62    memcpy(&sr, arg, sizeof(sr));
63    free(arg);
64
65    /* Initialize the response */
66    si.ident = sr.ident;
67    si.counter = 0;
68
69    /* Convert milliseconds into seconds+nanoseconds */
70    req.tv_sec = sr.interval / 1000;
71    req.tv_nsec = (sr.interval % 1000) * 1000000;
72
73    /* Block all signals */
74    sigfillset(&mask);
75    (void) pthread_sigmask(SIG_BLOCK, &mask, NULL);
76
77    for (;;) {
78
79        /* Sleep */
80        if (nanosleep(&req, &rem) < 0) {
81            //TODO: handle eintr, spurious wakeups
82            dbg_perror("nanosleep(2)");
83        }
84        si.counter++;
85        dbg_printf(" -------- sleep over (CTS=%d)----------", cts);
86
87        /* Test if the previous wakeup has been acknowledged */
88        if (!cts) {
89            cnt = read(sr.wfd, &buf, 1);
90            if (cnt < 0) {
91                if (errno == EAGAIN || errno == EWOULDBLOCK) {
92                    ;
93                } else {
94                    dbg_perror("read(2)");
95                    break;
96                }
97            } else if (cnt == 0) {
98                dbg_perror("short read(2)");
99                break;
100            } else {
101                cts = true;
102            }
103        }
104
105        /* Wake up kevent waiters if they are ready */
106        if (cts) {
107            cnt = write(sr.wfd, &si, sizeof(si));
108            if (cnt < 0) {
109                /* FIXME: handle EAGAIN and EINTR */
110                dbg_perror("write(2)");
111            } else if (cnt < sizeof(si)) {
112                dbg_puts("FIXME: handle short write");
113            }
114            cts = false;
115            si.counter = 0;
116        }
117    }
118
119    return (NULL);
120}
121
122static int
123_timer_create(struct filter *filt, struct knote *kn)
124{
125    pthread_attr_t attr;
126    struct sleepreq *req;
127    kn->kev.flags |= EV_CLEAR;
128
129    req = malloc(sizeof(*req));
130    if (req == NULL) {
131        dbg_perror("malloc");
132        return (-1);
133    }
134    req->pfd = filt->kf_pfd;
135    req->wfd = filt->kf_wfd;
136    req->ident = kn->kev.ident;
137    req->interval = kn->kev.data;
138
139    pthread_attr_init(&attr);
140    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
141    if (pthread_create(&kn->data.tid, &attr, sleeper_thread, req) != 0) {
142        dbg_perror("pthread_create");
143        pthread_attr_destroy(&attr);
144        free(req);
145        return (-1);
146    }
147    pthread_attr_destroy(&attr);
148
149    return (0);
150}
151
152static int
153_timer_delete(struct knote *kn)
154{
155    if (pthread_cancel(kn->data.tid) != 0) {
156        /* Race condition: sleeper_thread exits before it is cancelled */
157        if (errno == ENOENT)
158            return (0);
159        dbg_perror("pthread_cancel(3)");
160        return (-1);
161    }
162    return (0);
163}
164
165int
166evfilt_timer_init(struct filter *filt)
167{
168    int fd[2];
169
170    if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
171        dbg_perror("socketpair(3)");
172        return (-1);
173    }
174    if (fcntl(fd[0], F_SETFL, O_NONBLOCK) < 0
175        || fcntl(fd[1], F_SETFL, O_NONBLOCK) < 0) {
176        dbg_perror("fcntl(2)");
177        close(fd[0]);
178        close(fd[1]);
179        return (-1);
180    }
181
182    filt->kf_wfd = fd[0];
183    filt->kf_pfd = fd[1];
184
185    return (0);
186}
187
188void
189evfilt_timer_destroy(struct filter *filt)
190{
191    (void) close(filt->kf_wfd);
192    (void) close(filt->kf_pfd);
193}
194
195int
196evfilt_timer_copyout(struct filter *filt,
197            struct kevent *dst,
198            int nevents)
199{
200    struct sleepinfo    si;
201    ssize_t       cnt;
202    struct knote *kn;
203
204    /* Read the ident */
205    cnt = read(filt->kf_pfd, &si, sizeof(si));
206    if (cnt < 0) {
207        /* FIXME: handle EAGAIN and EINTR */
208        dbg_printf("read(2): %s", strerror(errno));
209        return (-1);
210    } else if (cnt < sizeof(si)) {
211        dbg_puts("error: short read");
212        return (-1);
213    }
214
215    /* Acknowlege receipt */
216    cnt = write(filt->kf_pfd, ".", 1);
217    if (cnt < 0) {
218        /* FIXME: handle EAGAIN and EINTR */
219        dbg_printf("write(2): %s", strerror(errno));
220        return (-1);
221    } else if (cnt < 1) {
222        dbg_puts("error: short write");
223        return (-1);
224    }
225
226    kn = knote_lookup(filt, si.ident);
227
228    /* Race condition: timer events remain queued even after
229       the knote is deleted. Ignore these events */
230    if (kn == NULL)
231        return (0);
232
233    dbg_printf("knote=%p", kn);
234    memcpy(dst, &kn->kev, sizeof(*dst));
235
236    dst->data = si.counter;
237
238    if (kn->kev.flags & EV_DISPATCH) {
239        KNOTE_DISABLE(kn);
240        _timer_delete(kn);
241    } else if (kn->kev.flags & EV_ONESHOT) {
242        _timer_delete(kn);
243        knote_free(filt, kn);
244    }
245
246    return (1);
247}
248
249int
250evfilt_timer_knote_create(struct filter *filt, struct knote *kn)
251{
252    return _timer_create(filt, kn);
253}
254
255int
256evfilt_timer_knote_modify(struct filter *filt, struct knote *kn,
257        const struct kevent *kev)
258{
259    return (-1); /* STUB */
260}
261
262int
263evfilt_timer_knote_delete(struct filter *filt, struct knote *kn)
264{
265    if (kn->kev.flags & EV_DISABLE)
266        return (0);
267
268    dbg_printf("deleting timer # %d", (int) kn->kev.ident);
269    return _timer_delete(kn);
270}
271
272int
273evfilt_timer_knote_enable(struct filter *filt, struct knote *kn)
274{
275    return evfilt_timer_knote_create(filt, kn);
276}
277
278int
279evfilt_timer_knote_disable(struct filter *filt, struct knote *kn)
280{
281    return evfilt_timer_knote_delete(filt, kn);
282}
283
284const struct filter evfilt_timer = {
285    EVFILT_TIMER,
286    evfilt_timer_init,
287    evfilt_timer_destroy,
288    evfilt_timer_copyout,
289    evfilt_timer_knote_create,
290    evfilt_timer_knote_modify,
291    evfilt_timer_knote_delete,
292    evfilt_timer_knote_enable,
293    evfilt_timer_knote_disable,
294};
295