/* evlist/evlist.c * * Event lists and basic devices. * * Copyright (C) 2003, 2004 * Andy Goth * * This code is available under the GNU General Public License; see COPYING. */ #include #include #include #include #include #include #include "evlist/evlist.h" #include "prjlibs/vec.h" static int evlist_sched_dispatch(evlist_t* el); static int evlist_sched_compar (const void* lhs, const void* rhs); int evlist_init(evlist_t* el) { VEC_INIT(device_t*, &el->devices); VEC_INIT(pollfd_t , &el->pollfds); VEC_INIT(sched_t , &el->scheds ); el->sched_id = 1; return 0; } int evlist_cleanup(evlist_t* el) { while (VEC_LEN(device_t*, &el->devices) != 0) { evlist_device_del(el, VEC_ELTS(device_t*, &el->devices)[0]); } VEC_FREE(device_t*, &el->devices); VEC_FREE(pollfd_t , &el->pollfds); VEC_FREE(sched_t , &el->scheds ); return 0; } int evlist_device_add(evlist_t* el, device_t* dev) { int i; dev->evlist = el; VEC_PUSH(device_t*, &el->devices, dev); if ((dev->hw_open != NULL && dev->hw_open(dev) == -1) || (dev->sw_open != NULL && dev->sw_open(dev) == -1)) { fprintf(stderr, "Failure opening %s (%s): %s\n", dev->name, dev->path, strerror(errno)); VEC_POP(device_t*, &el->devices); return -1; } else { return 0; } } int evlist_device_del(evlist_t* el, device_t* dev) { int i, ret; for (i = 0; i < VEC_LEN(device_t*, &el->devices); ++i) { if (VEC_ELTS(device_t*, &el->devices)[i] == dev) goto found; } return -1; found: if ((dev->sw_close != NULL && dev->sw_close(dev) == -1) || (dev->hw_close != NULL && dev->hw_close(dev) == -1)) { fprintf(stdout, "Failure closing %s (%s): %s\n", dev->name, dev->path, strerror(errno)); ret = -1; } else { ret = 0; } device_detatch_fd(dev, O_RDWR); VEC_CUT(device_t*, &el->devices, i, NULL, 1); return ret; } int evlist_sched_add(evlist_t* el, int time, schedcb_t func, void* arg, unsigned int* id) { int i; sched_t sched; gettimeofday(&sched.time, NULL); sched.func = func; sched.arg = arg ; sched.id = el->sched_id; if (time < 0) time = 0; sched.time.tv_sec += time / 1000; sched.time.tv_usec += (time % 1000) * 1000; if (sched.time.tv_usec > 1e6) { sched.time.tv_usec -= 1e6; ++sched.time.tv_sec; } VEC_INS(sched_t, &el->scheds, VEC_BSRCHF(sched_t, &el->scheds, &sched, evlist_sched_compar), &sched, 1); if (id != NULL) *id = el->sched_id; /* Determine next id. XXX: let's hope overflow never happens. */ ++el->sched_id; return 0; } int evlist_sched_del(evlist_t* el, unsigned int id) { int i; for (i = 0; i < VEC_LEN(sched_t, &el->scheds); ++i) { if (VEC_ELTS(sched_t, &el->scheds)[i].id == id) { VEC_CUT(sched_t, &el->scheds, i, NULL, 1); return 0; } } return -1; } int evlist_loop(evlist_t* el) { el->loop_exit = 0; while (!el->loop_exit && (VEC_LEN(pollfd_t, &el->pollfds) != 0 || VEC_LEN(sched_t , &el->scheds ) != 0)) { if (evlist_loop_next(el, -1) == -1) return -1; } return 0; } int evlist_loop_next(evlist_t* el, int _timeout) { int i, ret, len; timeval_t now, goal; int timeout, do_sched; device_t* dev; pollfd_t* pollfd; while (VEC_LEN(sched_t, &el->scheds) != 0) { gettimeofday(&now, NULL); goal = VEC_ELTS(sched_t, &el->scheds)[0].time; timeout = (goal.tv_sec - now.tv_sec ) * 1000 + (goal.tv_usec - now.tv_usec) / 1000; if (timeout > 0) { do_sched = 1; goto do_poll; } if (evlist_sched_dispatch(el) == -1) { fprintf(stderr, "Failure in timer callback: %s\n", strerror(errno)); return -1; } } timeout = -1; do_sched = 0; do_poll: if (do_sched == 0 && VEC_LEN(pollfd_t, &el->pollfds) == 0) { if (_timeout < 0) { /* Cowardly refuse to wait forever. */ return -1; } else { /* Wait for caller-supplied timeout to expire. */ timeout = _timeout; } } else { /* Adjust timeout. */ if (_timeout > -1 && timeout > _timeout) { timeout = _timeout; do_sched = 0; } } /* Wait for a file event or a timed event. */ ret = poll(VEC_ELTS(pollfd_t, &el->pollfds), VEC_LEN( pollfd_t, &el->pollfds), timeout); if (ret == -1) { /* Error. */ perror("poll"); return -1; } else if (ret == 0) { /* Timeout expired. */ if (do_sched) return evlist_sched_dispatch(el); else return 0; } /* File event. */ for (i = 0; i < VEC_LEN(device_t*, &el->devices); ++i) { dev = VEC_ELTS(device_t*, &el->devices)[i]; pollfd = dev->pollfd_in; if (pollfd != NULL && pollfd->revents != 0) { /* File is readable. */ if (pollfd->revents & POLLIN) { if ((dev->hw_readable != NULL && dev->hw_readable(dev) == -1) || (dev->sw_incoming != NULL && dev->sw_incoming(dev) == -1)) { if (errno == EPIPE) { goto close_device; } else { fprintf(stderr, "Failure reading from "); goto error; } } } else if (pollfd->revents & ~POLLOUT) { goto p_error; } } pollfd = dev->pollfd_out; if (pollfd != NULL && pollfd->revents != 0) { /* File is writable. */ if (pollfd->revents & POLLOUT) { if ((dev->sw_outgoing != NULL && dev->sw_outgoing(dev) == -1) || (dev->hw_writable != NULL && dev->hw_writable(dev) == -1)) { if (errno == EPIPE) { goto close_device; } else { fprintf(stderr, "Failure writing to "); goto error; } } } else if (pollfd->revents & ~POLLIN) { goto p_error; } } continue; close_device: evlist_device_del(el, dev); --i; } return 0; p_error: if (pollfd->revents & POLLHUP ) fprintf(stderr, "Hangup on " ); else if (pollfd->revents & POLLERR ) fprintf(stderr, "Error on " ); else if (pollfd->revents & POLLNVAL) fprintf(stderr, "Fd not open on "); else fprintf(stderr, "Unhandled event %u on ", pollfd->revents); error: fprintf(stderr, "%s (%s): %s\n", dev->name, dev->path, strerror(errno)); return -1; } int evlist_loop_exit(evlist_t* el) { el->loop_exit = 1; return 0; } static int evlist_sched_dispatch(evlist_t* el) { schedcb_t func; void* arg; if (VEC_LEN(sched_t, &el->scheds) != 0) { func = VEC_ELTS(sched_t, &el->scheds)[0].func; arg = VEC_ELTS(sched_t, &el->scheds)[0].arg ; VEC_SHIFTN(sched_t, &el->scheds, 1); if (func(arg) == -1) return -1; } return 0; } static int evlist_sched_compar(const void* lhs, const void* rhs) { timeval_t x = ((const sched_t*)lhs)->time; timeval_t y = ((const sched_t*)rhs)->time; if (x.tv_sec < y.tv_sec ) return -1; /* lhs < rhs */ else if (x.tv_sec > y.tv_sec ) return 1; /* lhs > rhs */ else if (x.tv_usec < y.tv_usec) return -1; /* lhs < rhs */ else if (x.tv_usec > y.tv_usec) return 1; /* lhs > rhs */ else return 0; /* lhs == rhs */ } /* vim: set ts=4 sts=4 sw=4 tw=80 et: */