diff options
Diffstat (limited to 'hw/dataplane/event-poll.c')
-rw-r--r-- | hw/dataplane/event-poll.c | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/hw/dataplane/event-poll.c b/hw/dataplane/event-poll.c new file mode 100644 index 0000000000..2b55c6e255 --- /dev/null +++ b/hw/dataplane/event-poll.c @@ -0,0 +1,100 @@ +/* + * Event loop with file descriptor polling + * + * Copyright 2012 IBM, Corp. + * Copyright 2012 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Hajnoczi <stefanha@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include <sys/epoll.h> +#include "hw/dataplane/event-poll.h" + +/* Add an event notifier and its callback for polling */ +void event_poll_add(EventPoll *poll, EventHandler *handler, + EventNotifier *notifier, EventCallback *callback) +{ + struct epoll_event event = { + .events = EPOLLIN, + .data.ptr = handler, + }; + handler->notifier = notifier; + handler->callback = callback; + if (epoll_ctl(poll->epoll_fd, EPOLL_CTL_ADD, + event_notifier_get_fd(notifier), &event) != 0) { + fprintf(stderr, "failed to add event handler to epoll: %m\n"); + exit(1); + } +} + +/* Event callback for stopping event_poll() */ +static void handle_stop(EventHandler *handler) +{ + /* Do nothing */ +} + +void event_poll_init(EventPoll *poll) +{ + /* Create epoll file descriptor */ + poll->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (poll->epoll_fd < 0) { + fprintf(stderr, "epoll_create1 failed: %m\n"); + exit(1); + } + + /* Set up stop notifier */ + if (event_notifier_init(&poll->stop_notifier, 0) < 0) { + fprintf(stderr, "failed to init stop notifier\n"); + exit(1); + } + event_poll_add(poll, &poll->stop_handler, + &poll->stop_notifier, handle_stop); +} + +void event_poll_cleanup(EventPoll *poll) +{ + event_notifier_cleanup(&poll->stop_notifier); + close(poll->epoll_fd); + poll->epoll_fd = -1; +} + +/* Block until the next event and invoke its callback */ +void event_poll(EventPoll *poll) +{ + EventHandler *handler; + struct epoll_event event; + int nevents; + + /* Wait for the next event. Only do one event per call to keep the + * function simple, this could be changed later. */ + do { + nevents = epoll_wait(poll->epoll_fd, &event, 1, -1); + } while (nevents < 0 && errno == EINTR); + if (unlikely(nevents != 1)) { + fprintf(stderr, "epoll_wait failed: %m\n"); + exit(1); /* should never happen */ + } + + /* Find out which event handler has become active */ + handler = event.data.ptr; + + /* Clear the eventfd */ + event_notifier_test_and_clear(handler->notifier); + + /* Handle the event */ + handler->callback(handler); +} + +/* Stop event_poll() + * + * This function can be used from another thread. + */ +void event_poll_notify(EventPoll *poll) +{ + event_notifier_set(&poll->stop_notifier); +} |