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>
		
			
				
	
	
		
			101 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			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);
 | 
						|
}
 |