/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sys/event.h" #include "private.h" #ifndef NDEBUG static char * itimerspec_dump(struct itimerspec *ts) { static char __thread buf[1024]; snprintf(buf, sizeof(buf), "itimer: [ interval=%lu s %lu ns, next expire=%lu s %lu ns ]", ts->it_interval.tv_sec, ts->it_interval.tv_nsec, ts->it_value.tv_sec, ts->it_value.tv_nsec ); return (buf); } #endif /* Convert milliseconds into seconds+nanoseconds */ static void convert_msec_to_itimerspec(struct itimerspec *dst, int src, int oneshot) { time_t sec, nsec; sec = src / 1000; nsec = (src % 1000) * 1000000; /* Set the interval */ if (oneshot) { dst->it_interval.tv_sec = 0; dst->it_interval.tv_nsec = 0; } else { dst->it_interval.tv_sec = sec; dst->it_interval.tv_nsec = nsec; } /* Set the initial expiration */ dst->it_value.tv_sec = sec; dst->it_value.tv_nsec = nsec; dbg_printf("%s", itimerspec_dump(dst)); } static int ktimer_create(struct filter *filt, struct knote *kn) { port_notify_t pn; struct sigevent se; struct itimerspec ts; timer_t timerid; kn->kev.flags |= EV_CLEAR; pn.portnfy_port = filt->kf_kqueue->kq_port; pn.portnfy_user = (void *) kn->kev.ident; se.sigev_notify = SIGEV_PORT; se.sigev_value.sival_ptr = &pn; if (timer_create (CLOCK_MONOTONIC, &se, &timerid) < 0) { dbg_perror("timer_create(2)"); return (-1); } convert_msec_to_itimerspec(&ts, kn->kev.data, kn->kev.flags & EV_ONESHOT); if (timer_settime(timerid, 0, &ts, NULL) < 0) { dbg_perror("timer_settime(2)"); (void) timer_delete(timerid); return (-1); } kn->data.timerid = timerid; dbg_printf("created timer with id #%lu", (unsigned long) timerid); return (0); } int evfilt_timer_init(struct filter *filt) { return (0); } void evfilt_timer_destroy(struct filter *filt) { return; } int evfilt_timer_copyout(struct filter *filt, struct kevent *dst, int nevents) { port_event_t *pe = (port_event_t *) pthread_getspecific(filt->kf_kqueue->kq_port_event); long buf; timer_t timerid; struct knote *kn; /* XXX-FIXME: danger here -- there has to be a better way */ buf = (long) pe->portev_user; timerid = (timer_t) buf; /* ^^^^^^^^^ */ kn = knote_lookup(filt, timerid); dbg_printf("knote=%p", kn); memcpy(dst, &kn->kev, sizeof(*dst)); //TODO: //if (ev->events & EPOLLERR) // dst->fflags = 1; /* FIXME: Return the actual timer error */ /* FIXME: On return, data contains the number of times the timer has been trigered. */ dst->data = 1; //workaround if (kn->kev.flags & EV_DISPATCH) { KNOTE_DISABLE(kn); timer_delete(kn->data.timerid); } else if (kn->kev.flags & EV_ONESHOT) { timer_delete(kn->data.timerid); knote_free(filt, kn); } return (1); } int evfilt_timer_knote_create(struct filter *filt, struct knote *kn) { return ktimer_create(filt, kn); } int evfilt_timer_knote_modify(struct filter *filt, struct knote *kn, const struct kevent *kev) { return (-1); /* STUB */ } int evfilt_timer_knote_delete(struct filter *filt, struct knote *kn) { if (kn->kev.flags & EV_DISABLE) return (0); dbg_printf("deleting timer # %d", kn->data.timerid); return timer_delete(kn->data.timerid); } int evfilt_timer_knote_enable(struct filter *filt, struct knote *kn) { return evfilt_timer_knote_create(filt, kn); } int evfilt_timer_knote_disable(struct filter *filt, struct knote *kn) { return evfilt_timer_knote_delete(filt, kn); } const struct filter evfilt_timer = { EVFILT_TIMER, evfilt_timer_init, evfilt_timer_destroy, evfilt_timer_copyout, evfilt_timer_knote_create, evfilt_timer_knote_modify, evfilt_timer_knote_delete, evfilt_timer_knote_enable, evfilt_timer_knote_disable, };