qemu-e2k/hw/dataplane/event-poll.c
Stefan Hajnoczi 71973b0461 dataplane: add event loop
Outside the safety of the global mutex we need to poll on file
descriptors.  I found epoll(2) is a convenient way to do that, although
other options could replace this module in the future (such as an
AioContext-based loop or glib's GMainLoop).

One important feature of this small event loop implementation is that
the loop can be terminated in a thread-safe way.  This allows QEMU to
stop the data plane thread cleanly.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2013-01-02 15:56:21 +01:00

101 lines
2.6 KiB
C

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