| /* |
| * 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); |
| } |