 71973b0461
			
		
	
	
		71973b0461
		
	
	
	
	
		
			
			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);
 | |
| }
 |