[libvirt] [PATCH 0/3] Expose the libvirtd event loop to apps as an API

We provide the virEventRegister() API to let applications provide a set of callbacks to integrate with their existing event loop. Not all apps have an existing event loop and forcing them to write one themselves is an undue burden, which they often get wrong. We have an event loop impl used by libvirtd that could be used by applications. This series exposes it via two new public APIs. Apps are free to ignore these new APIs and provide their own impl, but this should help the common case.

The daemon code calls virEventAddHandleImpl directly instead of calling the wrapper virEventAddHandle. * tools/console.c, daemon/libvirtd.c, daemon/mdns.c: Convert to use primary event APIs --- daemon/libvirtd.c | 47 ++++++++++++++++++++++++----------------------- daemon/mdns.c | 27 ++++++++++++++------------- tools/console.c | 31 ++++++++++++++++--------------- 3 files changed, 54 insertions(+), 51 deletions(-) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index b2e5e20..851e2e1 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -63,6 +63,7 @@ #include "remote_driver.h" #include "conf.h" #include "event.h" +#include "src/util/event.h" #include "memory.h" #include "stream.h" #include "hooks.h" @@ -1084,12 +1085,12 @@ static int qemudNetworkEnable(struct qemud_server *server) { sock = server->sockets; while (sock) { - if ((sock->watch = virEventAddHandleImpl(sock->fd, - VIR_EVENT_HANDLE_READABLE | - VIR_EVENT_HANDLE_ERROR | - VIR_EVENT_HANDLE_HANGUP, - qemudDispatchServerEvent, - server, NULL)) < 0) { + if ((sock->watch = virEventAddHandle(sock->fd, + VIR_EVENT_HANDLE_READABLE | + VIR_EVENT_HANDLE_ERROR | + VIR_EVENT_HANDLE_HANGUP, + qemudDispatchServerEvent, + server, NULL)) < 0) { VIR_ERROR0(_("Failed to add server event callback")); return -1; } @@ -1521,7 +1522,7 @@ error: */ void qemudDispatchClientFailure(struct qemud_client *client) { if (client->watch != -1) { - virEventRemoveHandleImpl(client->watch); + virEventRemoveHandle(client->watch); client->watch = -1; } @@ -2215,10 +2216,10 @@ int qemudRegisterClientEvent(struct qemud_server *server, mode = qemudCalculateHandleMode(client); - if ((client->watch = virEventAddHandleImpl(client->fd, - mode, - qemudDispatchClientEvent, - server, NULL)) < 0) + if ((client->watch = virEventAddHandle(client->fd, + mode, + qemudDispatchClientEvent, + server, NULL)) < 0) return -1; return 0; @@ -2232,7 +2233,7 @@ void qemudUpdateClientEvent(struct qemud_client *client) { mode = qemudCalculateHandleMode(client); - virEventUpdateHandleImpl(client->watch, mode); + virEventUpdateHandle(client->watch, mode); } @@ -2284,7 +2285,7 @@ static void qemudInactiveTimer(int timerid, void *data) { if (virStateActive() || server->clients) { VIR_DEBUG0("Timer expired but still active, not shutting down"); - virEventUpdateTimeoutImpl(timerid, -1); + virEventUpdateTimeout(timerid, -1); } else { VIR_DEBUG0("Timer expired and inactive, shutting down"); server->quitEventThread = 1; @@ -2327,9 +2328,9 @@ static void *qemudRunLoop(void *opaque) { virMutexLock(&server->lock); if (timeout > 0 && - (timerid = virEventAddTimeoutImpl(-1, - qemudInactiveTimer, - server, NULL)) < 0) { + (timerid = virEventAddTimeout(-1, + qemudInactiveTimer, + server, NULL)) < 0) { VIR_ERROR0(_("Failed to register shutdown timeout")); return NULL; } @@ -2358,14 +2359,14 @@ static void *qemudRunLoop(void *opaque) { if (timerActive) { if (server->clients) { VIR_DEBUG("Deactivating shutdown timer %d", timerid); - virEventUpdateTimeoutImpl(timerid, -1); + virEventUpdateTimeout(timerid, -1); timerActive = 0; } } else { if (!virStateActive() && !server->clients) { VIR_DEBUG("Activating shutdown timer %d", timerid); - virEventUpdateTimeoutImpl(timerid, timeout * 1000); + virEventUpdateTimeout(timerid, timeout * 1000); timerActive = 1; } } @@ -2481,7 +2482,7 @@ static void qemudCleanup(struct qemud_server *server) { while (sock) { struct qemud_socket *next = sock->next; if (sock->watch) - virEventRemoveHandleImpl(sock->watch); + virEventRemoveHandle(sock->watch); VIR_FORCE_CLOSE(sock->fd); /* Unlink unix domain sockets which are not in @@ -3022,10 +3023,10 @@ daemonSetupSignals(struct qemud_server *server) sig_action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sig_action, NULL); - if (virEventAddHandleImpl(sigpipe[0], - VIR_EVENT_HANDLE_READABLE, - qemudDispatchSignalEvent, - server, NULL) < 0) { + if (virEventAddHandle(sigpipe[0], + VIR_EVENT_HANDLE_READABLE, + qemudDispatchSignalEvent, + server, NULL) < 0) { VIR_ERROR0(_("Failed to register callback for signal pipe")); goto error; } diff --git a/daemon/mdns.c b/daemon/mdns.c index 8be3aa9..302141c 100644 --- a/daemon/mdns.c +++ b/daemon/mdns.c @@ -40,6 +40,7 @@ #include "libvirtd.h" #include "mdns.h" #include "event.h" +#include "src/util/event.h" #include "memory.h" #define AVAHI_DEBUG(fmt, ...) VIR_DEBUG(fmt, __VA_ARGS__) @@ -257,10 +258,10 @@ static AvahiWatch *libvirtd_mdns_watch_new(const AvahiPoll *api ATTRIBUTE_UNUSED AVAHI_DEBUG("New handle %p FD %d Event %d", w, w->fd, event); hEvents = virPollEventToEventHandleType(event); - if ((w->watch = virEventAddHandleImpl(fd, hEvents, - libvirtd_mdns_watch_dispatch, - w, - libvirtd_mdns_watch_dofree)) < 0) { + if ((w->watch = virEventAddHandle(fd, hEvents, + libvirtd_mdns_watch_dispatch, + w, + libvirtd_mdns_watch_dofree)) < 0) { VIR_FREE(w); return NULL; } @@ -271,7 +272,7 @@ static AvahiWatch *libvirtd_mdns_watch_new(const AvahiPoll *api ATTRIBUTE_UNUSED static void libvirtd_mdns_watch_update(AvahiWatch *w, AvahiWatchEvent event) { AVAHI_DEBUG("Update handle %p FD %d Event %d", w, w->fd, event); - virEventUpdateHandleImpl(w->watch, event); + virEventUpdateHandle(w->watch, event); } static AvahiWatchEvent libvirtd_mdns_watch_get_events(AvahiWatch *w) @@ -283,14 +284,14 @@ static AvahiWatchEvent libvirtd_mdns_watch_get_events(AvahiWatch *w) static void libvirtd_mdns_watch_free(AvahiWatch *w) { AVAHI_DEBUG("Free handle %p %d", w, w->fd); - virEventRemoveHandleImpl(w->watch); + virEventRemoveHandle(w->watch); } static void libvirtd_mdns_timeout_dispatch(int timer ATTRIBUTE_UNUSED, void *opaque) { AvahiTimeout *t = (AvahiTimeout*)opaque; AVAHI_DEBUG("Dispatch timeout %p %d", t, timer); - virEventUpdateTimeoutImpl(t->timer, -1); + virEventUpdateTimeout(t->timer, -1); t->callback(t, t->userdata); } @@ -329,10 +330,10 @@ static AvahiTimeout *libvirtd_mdns_timeout_new(const AvahiPoll *api ATTRIBUTE_UN timeout = -1; } - t->timer = virEventAddTimeoutImpl(timeout, - libvirtd_mdns_timeout_dispatch, - t, - libvirtd_mdns_timeout_dofree); + t->timer = virEventAddTimeout(timeout, + libvirtd_mdns_timeout_dispatch, + t, + libvirtd_mdns_timeout_dofree); t->callback = cb; t->userdata = userdata; @@ -364,13 +365,13 @@ static void libvirtd_mdns_timeout_update(AvahiTimeout *t, const struct timeval * timeout = -1; } - virEventUpdateTimeoutImpl(t->timer, timeout); + virEventUpdateTimeout(t->timer, timeout); } static void libvirtd_mdns_timeout_free(AvahiTimeout *t) { AVAHI_DEBUG("Free timeout %p", t); - virEventRemoveTimeoutImpl(t->timer); + virEventRemoveTimeout(t->timer); } diff --git a/tools/console.c b/tools/console.c index 224cf03..48a469e 100644 --- a/tools/console.c +++ b/tools/console.c @@ -44,6 +44,7 @@ # include "virterror_internal.h" # include "daemon/event.h" +# include "src/util/event.h" /* ie Ctrl-] as per telnet */ # define CTRL_CLOSE_BRACKET '\35' @@ -93,9 +94,9 @@ virConsoleShutdown(virConsolePtr con) con->quit = true; virStreamEventRemoveCallback(con->st); if (con->stdinWatch != -1) - virEventRemoveHandleImpl(con->stdinWatch); + virEventRemoveHandle(con->stdinWatch); if (con->stdinWatch != -1) - virEventRemoveHandleImpl(con->stdoutWatch); + virEventRemoveHandle(con->stdoutWatch); con->stdinWatch = -1; con->stdoutWatch = -1; } @@ -134,8 +135,8 @@ virConsoleEventOnStream(virStreamPtr st, } con->streamToTerminal.offset += got; if (con->streamToTerminal.offset) - virEventUpdateHandleImpl(con->stdoutWatch, - VIR_EVENT_HANDLE_WRITABLE); + virEventUpdateHandle(con->stdoutWatch, + VIR_EVENT_HANDLE_WRITABLE); } if (events & VIR_STREAM_EVENT_WRITABLE && @@ -266,7 +267,7 @@ virConsoleEventOnStdout(int watch ATTRIBUTE_UNUSED, } if (!con->streamToTerminal.offset) - virEventUpdateHandleImpl(con->stdoutWatch, 0); + virEventUpdateHandle(con->stdoutWatch, 0); if (events & VIR_EVENT_HANDLE_ERROR || events & VIR_EVENT_HANDLE_HANGUP) { @@ -331,16 +332,16 @@ int vshRunConsole(virDomainPtr dom, const char *devname) if (virDomainOpenConsole(dom, devname, con->st, 0) < 0) goto cleanup; - con->stdinWatch = virEventAddHandleImpl(STDIN_FILENO, - VIR_EVENT_HANDLE_READABLE, - virConsoleEventOnStdin, - con, - NULL); - con->stdoutWatch = virEventAddHandleImpl(STDOUT_FILENO, - 0, - virConsoleEventOnStdout, - con, - NULL); + con->stdinWatch = virEventAddHandle(STDIN_FILENO, + VIR_EVENT_HANDLE_READABLE, + virConsoleEventOnStdin, + con, + NULL); + con->stdoutWatch = virEventAddHandle(STDOUT_FILENO, + 0, + virConsoleEventOnStdout, + con, + NULL); virStreamEventAddCallback(con->st, VIR_STREAM_EVENT_READABLE, -- 1.7.4

On 03/03/2011 07:21 AM, Daniel P. Berrange wrote:
The daemon code calls virEventAddHandleImpl directly instead of calling the wrapper virEventAddHandle.
* tools/console.c, daemon/libvirtd.c, daemon/mdns.c: Convert to use primary event APIs --- daemon/libvirtd.c | 47 ++++++++++++++++++++++++----------------------- daemon/mdns.c | 27 ++++++++++++++------------- tools/console.c | 31 ++++++++++++++++--------------- 3 files changed, 54 insertions(+), 51 deletions(-)
ACK; looks pretty mechanical. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

The event loop implementation is used by more than just the daemon, so move it into the shared area. * daemon/event.c, src/util/event_poll.c: Renamed * daemon/event.h, src/util/event_poll.h: Renamed * tools/Makefile.am, tools/console.c, tools/virsh.c: Update to use new virEventPoll APIs * daemon/mdns.c, daemon/mdns.c, daemon/Makefile.am: Update to use new virEventPoll APIs --- daemon/Makefile.am | 1 - daemon/event.c | 706 ---------------------------------------------- daemon/event.h | 134 --------- daemon/libvirtd.c | 18 +- daemon/mdns.c | 6 +- src/Makefile.am | 1 + src/libvirt_private.syms | 13 + src/util/event_poll.c | 705 +++++++++++++++++++++++++++++++++++++++++++++ src/util/event_poll.h | 132 +++++++++ tools/Makefile.am | 1 - tools/console.c | 6 +- tools/virsh.c | 16 +- 12 files changed, 874 insertions(+), 865 deletions(-) delete mode 100644 daemon/event.c delete mode 100644 daemon/event.h create mode 100644 src/util/event_poll.c create mode 100644 src/util/event_poll.h diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 86f024f..4fbbc16 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -3,7 +3,6 @@ CLEANFILES = DAEMON_SOURCES = \ - event.c event.h \ libvirtd.c libvirtd.h \ remote.c remote.h \ dispatch.c dispatch.h \ diff --git a/daemon/event.c b/daemon/event.c deleted file mode 100644 index 1a31717..0000000 --- a/daemon/event.c +++ /dev/null @@ -1,706 +0,0 @@ -/* - * event.c: event loop for monitoring file handles - * - * Copyright (C) 2007, 2010-2011 Red Hat, Inc. - * Copyright (C) 2007 Daniel P. Berrange - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Daniel P. Berrange <berrange@redhat.com> - */ - -#include <config.h> - -#include <stdlib.h> -#include <string.h> -#include <poll.h> -#include <sys/time.h> -#include <errno.h> -#include <unistd.h> - -#include "threads.h" -#include "logging.h" -#include "event.h" -#include "memory.h" -#include "util.h" -#include "ignore-value.h" - -#define EVENT_DEBUG(fmt, ...) VIR_DEBUG(fmt, __VA_ARGS__) - -static int virEventInterruptLocked(void); - -/* State for a single file handle being monitored */ -struct virEventHandle { - int watch; - int fd; - int events; - virEventHandleCallback cb; - virFreeCallback ff; - void *opaque; - int deleted; -}; - -/* State for a single timer being generated */ -struct virEventTimeout { - int timer; - int frequency; - unsigned long long expiresAt; - virEventTimeoutCallback cb; - virFreeCallback ff; - void *opaque; - int deleted; -}; - -/* Allocate extra slots for virEventHandle/virEventTimeout - records in this multiple */ -#define EVENT_ALLOC_EXTENT 10 - -/* State for the main event loop */ -struct virEventLoop { - virMutex lock; - int running; - virThread leader; - int wakeupfd[2]; - size_t handlesCount; - size_t handlesAlloc; - struct virEventHandle *handles; - size_t timeoutsCount; - size_t timeoutsAlloc; - struct virEventTimeout *timeouts; -}; - -/* Only have one event loop */ -static struct virEventLoop eventLoop; - -/* Unique ID for the next FD watch to be registered */ -static int nextWatch = 1; - -/* Unique ID for the next timer to be registered */ -static int nextTimer = 1; - -/* - * Register a callback for monitoring file handle events. - * NB, it *must* be safe to call this from within a callback - * For this reason we only ever append to existing list. - */ -int virEventAddHandleImpl(int fd, int events, - virEventHandleCallback cb, - void *opaque, - virFreeCallback ff) { - int watch; - EVENT_DEBUG("Add handle fd=%d events=%d cb=%p opaque=%p", fd, events, cb, opaque); - virMutexLock(&eventLoop.lock); - if (eventLoop.handlesCount == eventLoop.handlesAlloc) { - EVENT_DEBUG("Used %zu handle slots, adding at least %d more", - eventLoop.handlesAlloc, EVENT_ALLOC_EXTENT); - if (VIR_RESIZE_N(eventLoop.handles, eventLoop.handlesAlloc, - eventLoop.handlesCount, EVENT_ALLOC_EXTENT) < 0) { - virMutexUnlock(&eventLoop.lock); - return -1; - } - } - - watch = nextWatch++; - - eventLoop.handles[eventLoop.handlesCount].watch = watch; - eventLoop.handles[eventLoop.handlesCount].fd = fd; - eventLoop.handles[eventLoop.handlesCount].events = - virEventHandleTypeToPollEvent(events); - eventLoop.handles[eventLoop.handlesCount].cb = cb; - eventLoop.handles[eventLoop.handlesCount].ff = ff; - eventLoop.handles[eventLoop.handlesCount].opaque = opaque; - eventLoop.handles[eventLoop.handlesCount].deleted = 0; - - eventLoop.handlesCount++; - - virEventInterruptLocked(); - virMutexUnlock(&eventLoop.lock); - - return watch; -} - -void virEventUpdateHandleImpl(int watch, int events) { - int i; - EVENT_DEBUG("Update handle w=%d e=%d", watch, events); - - if (watch <= 0) { - VIR_WARN("Ignoring invalid update watch %d", watch); - return; - } - - virMutexLock(&eventLoop.lock); - for (i = 0 ; i < eventLoop.handlesCount ; i++) { - if (eventLoop.handles[i].watch == watch) { - eventLoop.handles[i].events = - virEventHandleTypeToPollEvent(events); - virEventInterruptLocked(); - break; - } - } - virMutexUnlock(&eventLoop.lock); -} - -/* - * Unregister a callback from a file handle - * NB, it *must* be safe to call this from within a callback - * For this reason we only ever set a flag in the existing list. - * Actual deletion will be done out-of-band - */ -int virEventRemoveHandleImpl(int watch) { - int i; - EVENT_DEBUG("Remove handle w=%d", watch); - - if (watch <= 0) { - VIR_WARN("Ignoring invalid remove watch %d", watch); - return -1; - } - - virMutexLock(&eventLoop.lock); - for (i = 0 ; i < eventLoop.handlesCount ; i++) { - if (eventLoop.handles[i].deleted) - continue; - - if (eventLoop.handles[i].watch == watch) { - EVENT_DEBUG("mark delete %d %d", i, eventLoop.handles[i].fd); - eventLoop.handles[i].deleted = 1; - virEventInterruptLocked(); - virMutexUnlock(&eventLoop.lock); - return 0; - } - } - virMutexUnlock(&eventLoop.lock); - return -1; -} - - -/* - * Register a callback for a timer event - * NB, it *must* be safe to call this from within a callback - * For this reason we only ever append to existing list. - */ -int virEventAddTimeoutImpl(int frequency, - virEventTimeoutCallback cb, - void *opaque, - virFreeCallback ff) { - struct timeval now; - int ret; - EVENT_DEBUG("Adding timer %d with %d ms freq", nextTimer, frequency); - if (gettimeofday(&now, NULL) < 0) { - return -1; - } - - virMutexLock(&eventLoop.lock); - if (eventLoop.timeoutsCount == eventLoop.timeoutsAlloc) { - EVENT_DEBUG("Used %zu timeout slots, adding at least %d more", - eventLoop.timeoutsAlloc, EVENT_ALLOC_EXTENT); - if (VIR_RESIZE_N(eventLoop.timeouts, eventLoop.timeoutsAlloc, - eventLoop.timeoutsCount, EVENT_ALLOC_EXTENT) < 0) { - virMutexUnlock(&eventLoop.lock); - return -1; - } - } - - eventLoop.timeouts[eventLoop.timeoutsCount].timer = nextTimer++; - eventLoop.timeouts[eventLoop.timeoutsCount].frequency = frequency; - eventLoop.timeouts[eventLoop.timeoutsCount].cb = cb; - eventLoop.timeouts[eventLoop.timeoutsCount].ff = ff; - eventLoop.timeouts[eventLoop.timeoutsCount].opaque = opaque; - eventLoop.timeouts[eventLoop.timeoutsCount].deleted = 0; - eventLoop.timeouts[eventLoop.timeoutsCount].expiresAt = - frequency >= 0 ? frequency + - (((unsigned long long)now.tv_sec)*1000) + - (((unsigned long long)now.tv_usec)/1000) : 0; - - eventLoop.timeoutsCount++; - ret = nextTimer-1; - virEventInterruptLocked(); - virMutexUnlock(&eventLoop.lock); - return ret; -} - -void virEventUpdateTimeoutImpl(int timer, int frequency) { - struct timeval tv; - int i; - EVENT_DEBUG("Updating timer %d timeout with %d ms freq", timer, frequency); - - if (timer <= 0) { - VIR_WARN("Ignoring invalid update timer %d", timer); - return; - } - - if (gettimeofday(&tv, NULL) < 0) { - return; - } - - virMutexLock(&eventLoop.lock); - for (i = 0 ; i < eventLoop.timeoutsCount ; i++) { - if (eventLoop.timeouts[i].timer == timer) { - eventLoop.timeouts[i].frequency = frequency; - eventLoop.timeouts[i].expiresAt = - frequency >= 0 ? frequency + - (((unsigned long long)tv.tv_sec)*1000) + - (((unsigned long long)tv.tv_usec)/1000) : 0; - virEventInterruptLocked(); - break; - } - } - virMutexUnlock(&eventLoop.lock); -} - -/* - * Unregister a callback for a timer - * NB, it *must* be safe to call this from within a callback - * For this reason we only ever set a flag in the existing list. - * Actual deletion will be done out-of-band - */ -int virEventRemoveTimeoutImpl(int timer) { - int i; - EVENT_DEBUG("Remove timer %d", timer); - - if (timer <= 0) { - VIR_WARN("Ignoring invalid remove timer %d", timer); - return -1; - } - - virMutexLock(&eventLoop.lock); - for (i = 0 ; i < eventLoop.timeoutsCount ; i++) { - if (eventLoop.timeouts[i].deleted) - continue; - - if (eventLoop.timeouts[i].timer == timer) { - eventLoop.timeouts[i].deleted = 1; - virEventInterruptLocked(); - virMutexUnlock(&eventLoop.lock); - return 0; - } - } - virMutexUnlock(&eventLoop.lock); - return -1; -} - -/* Iterates over all registered timeouts and determine which - * will be the first to expire. - * @timeout: filled with expiry time of soonest timer, or -1 if - * no timeout is pending - * returns: 0 on success, -1 on error - */ -static int virEventCalculateTimeout(int *timeout) { - unsigned long long then = 0; - int i; - EVENT_DEBUG("Calculate expiry of %zu timers", eventLoop.timeoutsCount); - /* Figure out if we need a timeout */ - for (i = 0 ; i < eventLoop.timeoutsCount ; i++) { - if (eventLoop.timeouts[i].frequency < 0) - continue; - - EVENT_DEBUG("Got a timeout scheduled for %llu", eventLoop.timeouts[i].expiresAt); - if (then == 0 || - eventLoop.timeouts[i].expiresAt < then) - then = eventLoop.timeouts[i].expiresAt; - } - - /* Calculate how long we should wait for a timeout if needed */ - if (then > 0) { - struct timeval tv; - - if (gettimeofday(&tv, NULL) < 0) { - return -1; - } - - *timeout = then - - ((((unsigned long long)tv.tv_sec)*1000) + - (((unsigned long long)tv.tv_usec)/1000)); - - if (*timeout < 0) - *timeout = 0; - } else { - *timeout = -1; - } - - EVENT_DEBUG("Timeout at %llu due in %d ms", then, *timeout); - - return 0; -} - -/* - * Allocate a pollfd array containing data for all registered - * file handles. The caller must free the returned data struct - * returns: the pollfd array, or NULL on error - */ -static struct pollfd *virEventMakePollFDs(int *nfds) { - struct pollfd *fds; - int i; - - *nfds = 0; - for (i = 0 ; i < eventLoop.handlesCount ; i++) { - if (eventLoop.handles[i].events) - (*nfds)++; - } - - /* Setup the poll file handle data structs */ - if (VIR_ALLOC_N(fds, *nfds) < 0) - return NULL; - - *nfds = 0; - for (i = 0 ; i < eventLoop.handlesCount ; i++) { - EVENT_DEBUG("Prepare n=%d w=%d, f=%d e=%d", i, - eventLoop.handles[i].watch, - eventLoop.handles[i].fd, - eventLoop.handles[i].events); - if (!eventLoop.handles[i].events) - continue; - fds[*nfds].fd = eventLoop.handles[i].fd; - fds[*nfds].events = eventLoop.handles[i].events; - fds[*nfds].revents = 0; - (*nfds)++; - //EVENT_DEBUG("Wait for %d %d", eventLoop.handles[i].fd, eventLoop.handles[i].events); - } - - return fds; -} - - -/* - * Iterate over all timers and determine if any have expired. - * Invoke the user supplied callback for each timer whose - * expiry time is met, and schedule the next timeout. Does - * not try to 'catch up' on time if the actual expiry time - * was later than the requested time. - * - * This method must cope with new timers being registered - * by a callback, and must skip any timers marked as deleted. - * - * Returns 0 upon success, -1 if an error occurred - */ -static int virEventDispatchTimeouts(void) { - struct timeval tv; - unsigned long long now; - int i; - /* Save this now - it may be changed during dispatch */ - int ntimeouts = eventLoop.timeoutsCount; - VIR_DEBUG("Dispatch %d", ntimeouts); - - if (gettimeofday(&tv, NULL) < 0) { - return -1; - } - now = (((unsigned long long)tv.tv_sec)*1000) + - (((unsigned long long)tv.tv_usec)/1000); - - for (i = 0 ; i < ntimeouts ; i++) { - if (eventLoop.timeouts[i].deleted || eventLoop.timeouts[i].frequency < 0) - continue; - - /* Add 20ms fuzz so we don't pointlessly spin doing - * <10ms sleeps, particularly on kernels with low HZ - * it is fine that a timer expires 20ms earlier than - * requested - */ - if (eventLoop.timeouts[i].expiresAt <= (now+20)) { - virEventTimeoutCallback cb = eventLoop.timeouts[i].cb; - int timer = eventLoop.timeouts[i].timer; - void *opaque = eventLoop.timeouts[i].opaque; - eventLoop.timeouts[i].expiresAt = - now + eventLoop.timeouts[i].frequency; - - virMutexUnlock(&eventLoop.lock); - (cb)(timer, opaque); - virMutexLock(&eventLoop.lock); - } - } - return 0; -} - - -/* Iterate over all file handles and dispatch any which - * have pending events listed in the poll() data. Invoke - * the user supplied callback for each handle which has - * pending events - * - * This method must cope with new handles being registered - * by a callback, and must skip any handles marked as deleted. - * - * Returns 0 upon success, -1 if an error occurred - */ -static int virEventDispatchHandles(int nfds, struct pollfd *fds) { - int i, n; - VIR_DEBUG("Dispatch %d", nfds); - - /* NB, use nfds not eventLoop.handlesCount, because new - * fds might be added on end of list, and they're not - * in the fds array we've got */ - for (i = 0, n = 0 ; n < nfds && i < eventLoop.handlesCount ; n++) { - while ((eventLoop.handles[i].fd != fds[n].fd || - eventLoop.handles[i].events == 0) && - i < eventLoop.handlesCount) { - i++; - } - if (i == eventLoop.handlesCount) - break; - - VIR_DEBUG("i=%d w=%d", i, eventLoop.handles[i].watch); - if (eventLoop.handles[i].deleted) { - EVENT_DEBUG("Skip deleted n=%d w=%d f=%d", i, - eventLoop.handles[i].watch, eventLoop.handles[i].fd); - continue; - } - - if (fds[n].revents) { - virEventHandleCallback cb = eventLoop.handles[i].cb; - int watch = eventLoop.handles[i].watch; - void *opaque = eventLoop.handles[i].opaque; - int hEvents = virPollEventToEventHandleType(fds[n].revents); - EVENT_DEBUG("Dispatch n=%d f=%d w=%d e=%d %p", i, - fds[n].fd, watch, fds[n].revents, opaque); - virMutexUnlock(&eventLoop.lock); - (cb)(watch, fds[n].fd, hEvents, opaque); - virMutexLock(&eventLoop.lock); - } - } - - return 0; -} - - -/* Used post dispatch to actually remove any timers that - * were previously marked as deleted. This asynchronous - * cleanup is needed to make dispatch re-entrant safe. - */ -static int virEventCleanupTimeouts(void) { - int i; - size_t gap; - VIR_DEBUG("Cleanup %zu", eventLoop.timeoutsCount); - - /* Remove deleted entries, shuffling down remaining - * entries as needed to form contiguous series - */ - for (i = 0 ; i < eventLoop.timeoutsCount ; ) { - if (!eventLoop.timeouts[i].deleted) { - i++; - continue; - } - - EVENT_DEBUG("Purging timeout %d with id %d", i, - eventLoop.timeouts[i].timer); - if (eventLoop.timeouts[i].ff) - (eventLoop.timeouts[i].ff)(eventLoop.timeouts[i].opaque); - - if ((i+1) < eventLoop.timeoutsCount) { - memmove(eventLoop.timeouts+i, - eventLoop.timeouts+i+1, - sizeof(struct virEventTimeout)*(eventLoop.timeoutsCount - -(i+1))); - } - eventLoop.timeoutsCount--; - } - - /* Release some memory if we've got a big chunk free */ - gap = eventLoop.timeoutsAlloc - eventLoop.timeoutsCount; - if (eventLoop.timeoutsCount == 0 || - (gap > eventLoop.timeoutsCount && gap > EVENT_ALLOC_EXTENT)) { - EVENT_DEBUG("Found %zu out of %zu timeout slots used, releasing %zu", - eventLoop.timeoutsCount, eventLoop.timeoutsAlloc, gap); - VIR_SHRINK_N(eventLoop.timeouts, eventLoop.timeoutsAlloc, gap); - } - return 0; -} - -/* Used post dispatch to actually remove any handles that - * were previously marked as deleted. This asynchronous - * cleanup is needed to make dispatch re-entrant safe. - */ -static int virEventCleanupHandles(void) { - int i; - size_t gap; - VIR_DEBUG("Cleanup %zu", eventLoop.handlesCount); - - /* Remove deleted entries, shuffling down remaining - * entries as needed to form contiguous series - */ - for (i = 0 ; i < eventLoop.handlesCount ; ) { - if (!eventLoop.handles[i].deleted) { - i++; - continue; - } - - if (eventLoop.handles[i].ff) - (eventLoop.handles[i].ff)(eventLoop.handles[i].opaque); - - if ((i+1) < eventLoop.handlesCount) { - memmove(eventLoop.handles+i, - eventLoop.handles+i+1, - sizeof(struct virEventHandle)*(eventLoop.handlesCount - -(i+1))); - } - eventLoop.handlesCount--; - } - - /* Release some memory if we've got a big chunk free */ - gap = eventLoop.handlesAlloc - eventLoop.handlesCount; - if (eventLoop.handlesCount == 0 || - (gap > eventLoop.handlesCount && gap > EVENT_ALLOC_EXTENT)) { - EVENT_DEBUG("Found %zu out of %zu handles slots used, releasing %zu", - eventLoop.handlesCount, eventLoop.handlesAlloc, gap); - VIR_SHRINK_N(eventLoop.handles, eventLoop.handlesAlloc, gap); - } - return 0; -} - -/* - * Run a single iteration of the event loop, blocking until - * at least one file handle has an event, or a timer expires - */ -int virEventRunOnce(void) { - struct pollfd *fds = NULL; - int ret, timeout, nfds; - - virMutexLock(&eventLoop.lock); - eventLoop.running = 1; - virThreadSelf(&eventLoop.leader); - - if (virEventCleanupTimeouts() < 0 || - virEventCleanupHandles() < 0) - goto error; - - if (!(fds = virEventMakePollFDs(&nfds)) || - virEventCalculateTimeout(&timeout) < 0) - goto error; - - virMutexUnlock(&eventLoop.lock); - - retry: - EVENT_DEBUG("Poll on %d handles %p timeout %d", nfds, fds, timeout); - ret = poll(fds, nfds, timeout); - if (ret < 0) { - EVENT_DEBUG("Poll got error event %d", errno); - if (errno == EINTR) { - goto retry; - } - goto error_unlocked; - } - EVENT_DEBUG("Poll got %d event(s)", ret); - - virMutexLock(&eventLoop.lock); - if (virEventDispatchTimeouts() < 0) - goto error; - - if (ret > 0 && - virEventDispatchHandles(nfds, fds) < 0) - goto error; - - if (virEventCleanupTimeouts() < 0 || - virEventCleanupHandles() < 0) - goto error; - - eventLoop.running = 0; - virMutexUnlock(&eventLoop.lock); - VIR_FREE(fds); - return 0; - -error: - virMutexUnlock(&eventLoop.lock); -error_unlocked: - VIR_FREE(fds); - return -1; -} - - -static void virEventHandleWakeup(int watch ATTRIBUTE_UNUSED, - int fd, - int events ATTRIBUTE_UNUSED, - void *opaque ATTRIBUTE_UNUSED) -{ - char c; - virMutexLock(&eventLoop.lock); - ignore_value(saferead(fd, &c, sizeof(c))); - virMutexUnlock(&eventLoop.lock); -} - -int virEventInit(void) -{ - if (virMutexInit(&eventLoop.lock) < 0) - return -1; - - if (pipe(eventLoop.wakeupfd) < 0 || - virSetNonBlock(eventLoop.wakeupfd[0]) < 0 || - virSetNonBlock(eventLoop.wakeupfd[1]) < 0 || - virSetCloseExec(eventLoop.wakeupfd[0]) < 0 || - virSetCloseExec(eventLoop.wakeupfd[1]) < 0) - return -1; - - if (virEventAddHandleImpl(eventLoop.wakeupfd[0], - VIR_EVENT_HANDLE_READABLE, - virEventHandleWakeup, NULL, NULL) < 0) - return -1; - - return 0; -} - -static int virEventInterruptLocked(void) -{ - char c = '\0'; - - if (!eventLoop.running || - virThreadIsSelf(&eventLoop.leader)) { - VIR_DEBUG("Skip interrupt, %d %d", eventLoop.running, - virThreadID(&eventLoop.leader)); - return 0; - } - - VIR_DEBUG0("Interrupting"); - if (safewrite(eventLoop.wakeupfd[1], &c, sizeof(c)) != sizeof(c)) - return -1; - return 0; -} - -int virEventInterrupt(void) -{ - int ret; - virMutexLock(&eventLoop.lock); - ret = virEventInterruptLocked(); - virMutexUnlock(&eventLoop.lock); - return ret; -} - -int -virEventHandleTypeToPollEvent(int events) -{ - int ret = 0; - if(events & VIR_EVENT_HANDLE_READABLE) - ret |= POLLIN; - if(events & VIR_EVENT_HANDLE_WRITABLE) - ret |= POLLOUT; - if(events & VIR_EVENT_HANDLE_ERROR) - ret |= POLLERR; - if(events & VIR_EVENT_HANDLE_HANGUP) - ret |= POLLHUP; - return ret; -} - -int -virPollEventToEventHandleType(int events) -{ - int ret = 0; - if(events & POLLIN) - ret |= VIR_EVENT_HANDLE_READABLE; - if(events & POLLOUT) - ret |= VIR_EVENT_HANDLE_WRITABLE; - if(events & POLLERR) - ret |= VIR_EVENT_HANDLE_ERROR; - if(events & POLLNVAL) /* Treat NVAL as error, since libvirt doesn't distinguish */ - ret |= VIR_EVENT_HANDLE_ERROR; - if(events & POLLHUP) - ret |= VIR_EVENT_HANDLE_HANGUP; - return ret; -} diff --git a/daemon/event.h b/daemon/event.h deleted file mode 100644 index dc03589..0000000 --- a/daemon/event.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * event.h: event loop for monitoring file handles - * - * Copyright (C) 2007 Daniel P. Berrange - * Copyright (C) 2007 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Daniel P. Berrange <berrange@redhat.com> - */ - -#ifndef __VIRTD_EVENT_H__ -# define __VIRTD_EVENT_H__ - -# include "internal.h" - -/** - * virEventAddHandleImpl: register a callback for monitoring file handle events - * - * @fd: file handle to monitor for events - * @events: bitset of events to watch from POLLnnn constants - * @cb: callback to invoke when an event occurs - * @opaque: user data to pass to callback - * - * returns -1 if the file handle cannot be registered, 0 upon success - */ -int virEventAddHandleImpl(int fd, int events, - virEventHandleCallback cb, - void *opaque, - virFreeCallback ff); - -/** - * virEventUpdateHandleImpl: change event set for a monitored file handle - * - * @watch: watch whose handle to update - * @events: bitset of events to watch from POLLnnn constants - * - * Will not fail if fd exists - */ -void virEventUpdateHandleImpl(int watch, int events); - -/** - * virEventRemoveHandleImpl: unregister a callback from a file handle - * - * @watch: watch whose handle to remove - * - * returns -1 if the file handle was not registered, 0 upon success - */ -int virEventRemoveHandleImpl(int watch); - -/** - * virEventAddTimeoutImpl: register a callback for a timer event - * - * @frequency: time between events in milliseconds - * @cb: callback to invoke when an event occurs - * @opaque: user data to pass to callback - * - * Setting frequency to -1 will disable the timer. Setting the frequency - * to zero will cause it to fire on every event loop iteration. - * - * returns -1 if the file handle cannot be registered, a positive - * integer timer id upon success - */ -int virEventAddTimeoutImpl(int frequency, - virEventTimeoutCallback cb, - void *opaque, - virFreeCallback ff); - -/** - * virEventUpdateTimeoutImpl: change frequency for a timer - * - * @timer: timer id to change - * @frequency: time between events in milliseconds - * - * Setting frequency to -1 will disable the timer. Setting the frequency - * to zero will cause it to fire on every event loop iteration. - * - * Will not fail if timer exists - */ -void virEventUpdateTimeoutImpl(int timer, int frequency); - -/** - * virEventRemoveTimeoutImpl: unregister a callback for a timer - * - * @timer: the timer id to remove - * - * returns -1 if the timer was not registered, 0 upon success - */ -int virEventRemoveTimeoutImpl(int timer); - -/** - * virEventInit: Initialize the event loop - * - * returns -1 if initialization failed - */ -int virEventInit(void); - -/** - * virEventRunOnce: run a single iteration of the event loop. - * - * Blocks the caller until at least one file handle has an - * event or the first timer expires. - * - * returns -1 if the event monitoring failed - */ -int virEventRunOnce(void); - -int -virEventHandleTypeToPollEvent(int events); -int -virPollEventToEventHandleType(int events); - - -/** - * virEventInterrupt: wakeup any thread waiting in poll() - * - * return -1 if wakup failed - */ -int virEventInterrupt(void); - - -#endif /* __VIRTD_EVENT_H__ */ diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 851e2e1..fdc9180 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -63,7 +63,7 @@ #include "remote_driver.h" #include "conf.h" #include "event.h" -#include "src/util/event.h" +#include "event_poll.h" #include "memory.h" #include "stream.h" #include "hooks.h" @@ -873,7 +873,7 @@ static struct qemud_server *qemudInitialize(void) { return NULL; } - if (virEventInit() < 0) { + if (virEventPollInit() < 0) { VIR_ERROR0(_("Failed to initialize event system")); virMutexDestroy(&server->lock); if (virCondDestroy(&server->job) < 0) @@ -937,12 +937,12 @@ static struct qemud_server *qemudInitialize(void) { # endif #endif - virEventRegisterImpl(virEventAddHandleImpl, - virEventUpdateHandleImpl, - virEventRemoveHandleImpl, - virEventAddTimeoutImpl, - virEventUpdateTimeoutImpl, - virEventRemoveTimeoutImpl); + virEventRegisterImpl(virEventPollAddHandle, + virEventPollUpdateHandle, + virEventPollRemoveHandle, + virEventPollAddTimeout, + virEventPollUpdateTimeout, + virEventPollRemoveTimeout); return server; } @@ -2263,7 +2263,7 @@ qemudDispatchServerEvent(int watch, int fd, int events, void *opaque) { static int qemudOneLoop(void) { sig_atomic_t errors; - if (virEventRunOnce() < 0) + if (virEventPollRunOnce() < 0) return -1; /* Check for any signal handling errors and log them. */ diff --git a/daemon/mdns.c b/daemon/mdns.c index 302141c..03695fd 100644 --- a/daemon/mdns.c +++ b/daemon/mdns.c @@ -40,7 +40,7 @@ #include "libvirtd.h" #include "mdns.h" #include "event.h" -#include "src/util/event.h" +#include "event_poll.h" #include "memory.h" #define AVAHI_DEBUG(fmt, ...) VIR_DEBUG(fmt, __VA_ARGS__) @@ -231,7 +231,7 @@ static void libvirtd_mdns_client_callback(AvahiClient *c, AvahiClientState state static void libvirtd_mdns_watch_dispatch(int watch, int fd, int events, void *opaque) { AvahiWatch *w = (AvahiWatch*)opaque; - int fd_events = virEventHandleTypeToPollEvent(events); + int fd_events = virEventPollToNativeEvents(events); AVAHI_DEBUG("Dispatch watch %d FD %d Event %d", watch, fd, fd_events); w->revents = fd_events; w->callback(w, fd, fd_events, w->userdata); @@ -257,7 +257,7 @@ static AvahiWatch *libvirtd_mdns_watch_new(const AvahiPoll *api ATTRIBUTE_UNUSED w->userdata = userdata; AVAHI_DEBUG("New handle %p FD %d Event %d", w, w->fd, event); - hEvents = virPollEventToEventHandleType(event); + hEvents = virEventPollFromNativeEvents(event); if ((w->watch = virEventAddHandle(fd, hEvents, libvirtd_mdns_watch_dispatch, w, diff --git a/src/Makefile.am b/src/Makefile.am index bd25b38..25f0e48 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,6 +52,7 @@ UTIL_SOURCES = \ util/conf.c util/conf.h \ util/cgroup.c util/cgroup.h \ util/event.c util/event.h \ + util/event_poll.c util/event_poll.h \ util/files.c util/files.h \ util/hash.c util/hash.h \ util/hooks.c util/hooks.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 5e63a12..5c7f4df 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -393,6 +393,19 @@ virEventUpdateHandle; virEventUpdateTimeout; +# event_poll.h +virEventPollToNativeEvents; +virEventPollFromNativeEvents; +virEventPollRunOnce; +virEventPollInit; +virEventPollRemoveTimeout; +virEventPollUpdateTimeout; +virEventPollAddTimeout; +virEventPollRemoveHandle; +virEventPollUpdateHandle; +virEventPollAddHandle; + + # fdstream.h virFDStreamOpen; virFDStreamConnectUNIX; diff --git a/src/util/event_poll.c b/src/util/event_poll.c new file mode 100644 index 0000000..1362840 --- /dev/null +++ b/src/util/event_poll.c @@ -0,0 +1,705 @@ +/* + * event.c: event loop for monitoring file handles + * + * Copyright (C) 2007, 2010-2011 Red Hat, Inc. + * Copyright (C) 2007 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <stdlib.h> +#include <string.h> +#include <poll.h> +#include <sys/time.h> +#include <errno.h> +#include <unistd.h> + +#include "threads.h" +#include "logging.h" +#include "event_poll.h" +#include "memory.h" +#include "util.h" +#include "ignore-value.h" + +#define EVENT_DEBUG(fmt, ...) VIR_DEBUG(fmt, __VA_ARGS__) + +static int virEventPollInterruptLocked(void); + +/* State for a single file handle being monitored */ +struct virEventPollHandle { + int watch; + int fd; + int events; + virEventHandleCallback cb; + virFreeCallback ff; + void *opaque; + int deleted; +}; + +/* State for a single timer being generated */ +struct virEventPollTimeout { + int timer; + int frequency; + unsigned long long expiresAt; + virEventTimeoutCallback cb; + virFreeCallback ff; + void *opaque; + int deleted; +}; + +/* Allocate extra slots for virEventPollHandle/virEventPollTimeout + records in this multiple */ +#define EVENT_ALLOC_EXTENT 10 + +/* State for the main event loop */ +struct virEventPollLoop { + virMutex lock; + int running; + virThread leader; + int wakeupfd[2]; + size_t handlesCount; + size_t handlesAlloc; + struct virEventPollHandle *handles; + size_t timeoutsCount; + size_t timeoutsAlloc; + struct virEventPollTimeout *timeouts; +}; + +/* Only have one event loop */ +static struct virEventPollLoop eventLoop; + +/* Unique ID for the next FD watch to be registered */ +static int nextWatch = 1; + +/* Unique ID for the next timer to be registered */ +static int nextTimer = 1; + +/* + * Register a callback for monitoring file handle events. + * NB, it *must* be safe to call this from within a callback + * For this reason we only ever append to existing list. + */ +int virEventPollAddHandle(int fd, int events, + virEventHandleCallback cb, + void *opaque, + virFreeCallback ff) { + int watch; + EVENT_DEBUG("Add handle fd=%d events=%d cb=%p opaque=%p", fd, events, cb, opaque); + virMutexLock(&eventLoop.lock); + if (eventLoop.handlesCount == eventLoop.handlesAlloc) { + EVENT_DEBUG("Used %zu handle slots, adding at least %d more", + eventLoop.handlesAlloc, EVENT_ALLOC_EXTENT); + if (VIR_RESIZE_N(eventLoop.handles, eventLoop.handlesAlloc, + eventLoop.handlesCount, EVENT_ALLOC_EXTENT) < 0) { + virMutexUnlock(&eventLoop.lock); + return -1; + } + } + + watch = nextWatch++; + + eventLoop.handles[eventLoop.handlesCount].watch = watch; + eventLoop.handles[eventLoop.handlesCount].fd = fd; + eventLoop.handles[eventLoop.handlesCount].events = + virEventPollToNativeEvents(events); + eventLoop.handles[eventLoop.handlesCount].cb = cb; + eventLoop.handles[eventLoop.handlesCount].ff = ff; + eventLoop.handles[eventLoop.handlesCount].opaque = opaque; + eventLoop.handles[eventLoop.handlesCount].deleted = 0; + + eventLoop.handlesCount++; + + virEventPollInterruptLocked(); + virMutexUnlock(&eventLoop.lock); + + return watch; +} + +void virEventPollUpdateHandle(int watch, int events) { + int i; + EVENT_DEBUG("Update handle w=%d e=%d", watch, events); + + if (watch <= 0) { + VIR_WARN("Ignoring invalid update watch %d", watch); + return; + } + + virMutexLock(&eventLoop.lock); + for (i = 0 ; i < eventLoop.handlesCount ; i++) { + if (eventLoop.handles[i].watch == watch) { + eventLoop.handles[i].events = + virEventPollToNativeEvents(events); + virEventPollInterruptLocked(); + break; + } + } + virMutexUnlock(&eventLoop.lock); +} + +/* + * Unregister a callback from a file handle + * NB, it *must* be safe to call this from within a callback + * For this reason we only ever set a flag in the existing list. + * Actual deletion will be done out-of-band + */ +int virEventPollRemoveHandle(int watch) { + int i; + EVENT_DEBUG("Remove handle w=%d", watch); + + if (watch <= 0) { + VIR_WARN("Ignoring invalid remove watch %d", watch); + return -1; + } + + virMutexLock(&eventLoop.lock); + for (i = 0 ; i < eventLoop.handlesCount ; i++) { + if (eventLoop.handles[i].deleted) + continue; + + if (eventLoop.handles[i].watch == watch) { + EVENT_DEBUG("mark delete %d %d", i, eventLoop.handles[i].fd); + eventLoop.handles[i].deleted = 1; + virEventPollInterruptLocked(); + virMutexUnlock(&eventLoop.lock); + return 0; + } + } + virMutexUnlock(&eventLoop.lock); + return -1; +} + + +/* + * Register a callback for a timer event + * NB, it *must* be safe to call this from within a callback + * For this reason we only ever append to existing list. + */ +int virEventPollAddTimeout(int frequency, + virEventTimeoutCallback cb, + void *opaque, + virFreeCallback ff) { + struct timeval now; + int ret; + EVENT_DEBUG("Adding timer %d with %d ms freq", nextTimer, frequency); + if (gettimeofday(&now, NULL) < 0) { + return -1; + } + + virMutexLock(&eventLoop.lock); + if (eventLoop.timeoutsCount == eventLoop.timeoutsAlloc) { + EVENT_DEBUG("Used %zu timeout slots, adding at least %d more", + eventLoop.timeoutsAlloc, EVENT_ALLOC_EXTENT); + if (VIR_RESIZE_N(eventLoop.timeouts, eventLoop.timeoutsAlloc, + eventLoop.timeoutsCount, EVENT_ALLOC_EXTENT) < 0) { + virMutexUnlock(&eventLoop.lock); + return -1; + } + } + + eventLoop.timeouts[eventLoop.timeoutsCount].timer = nextTimer++; + eventLoop.timeouts[eventLoop.timeoutsCount].frequency = frequency; + eventLoop.timeouts[eventLoop.timeoutsCount].cb = cb; + eventLoop.timeouts[eventLoop.timeoutsCount].ff = ff; + eventLoop.timeouts[eventLoop.timeoutsCount].opaque = opaque; + eventLoop.timeouts[eventLoop.timeoutsCount].deleted = 0; + eventLoop.timeouts[eventLoop.timeoutsCount].expiresAt = + frequency >= 0 ? frequency + + (((unsigned long long)now.tv_sec)*1000) + + (((unsigned long long)now.tv_usec)/1000) : 0; + + eventLoop.timeoutsCount++; + ret = nextTimer-1; + virEventPollInterruptLocked(); + virMutexUnlock(&eventLoop.lock); + return ret; +} + +void virEventPollUpdateTimeout(int timer, int frequency) { + struct timeval tv; + int i; + EVENT_DEBUG("Updating timer %d timeout with %d ms freq", timer, frequency); + + if (timer <= 0) { + VIR_WARN("Ignoring invalid update timer %d", timer); + return; + } + + if (gettimeofday(&tv, NULL) < 0) { + return; + } + + virMutexLock(&eventLoop.lock); + for (i = 0 ; i < eventLoop.timeoutsCount ; i++) { + if (eventLoop.timeouts[i].timer == timer) { + eventLoop.timeouts[i].frequency = frequency; + eventLoop.timeouts[i].expiresAt = + frequency >= 0 ? frequency + + (((unsigned long long)tv.tv_sec)*1000) + + (((unsigned long long)tv.tv_usec)/1000) : 0; + virEventPollInterruptLocked(); + break; + } + } + virMutexUnlock(&eventLoop.lock); +} + +/* + * Unregister a callback for a timer + * NB, it *must* be safe to call this from within a callback + * For this reason we only ever set a flag in the existing list. + * Actual deletion will be done out-of-band + */ +int virEventPollRemoveTimeout(int timer) { + int i; + EVENT_DEBUG("Remove timer %d", timer); + + if (timer <= 0) { + VIR_WARN("Ignoring invalid remove timer %d", timer); + return -1; + } + + virMutexLock(&eventLoop.lock); + for (i = 0 ; i < eventLoop.timeoutsCount ; i++) { + if (eventLoop.timeouts[i].deleted) + continue; + + if (eventLoop.timeouts[i].timer == timer) { + eventLoop.timeouts[i].deleted = 1; + virEventPollInterruptLocked(); + virMutexUnlock(&eventLoop.lock); + return 0; + } + } + virMutexUnlock(&eventLoop.lock); + return -1; +} + +/* Iterates over all registered timeouts and determine which + * will be the first to expire. + * @timeout: filled with expiry time of soonest timer, or -1 if + * no timeout is pending + * returns: 0 on success, -1 on error + */ +static int virEventPollCalculateTimeout(int *timeout) { + unsigned long long then = 0; + int i; + EVENT_DEBUG("Calculate expiry of %zu timers", eventLoop.timeoutsCount); + /* Figure out if we need a timeout */ + for (i = 0 ; i < eventLoop.timeoutsCount ; i++) { + if (eventLoop.timeouts[i].frequency < 0) + continue; + + EVENT_DEBUG("Got a timeout scheduled for %llu", eventLoop.timeouts[i].expiresAt); + if (then == 0 || + eventLoop.timeouts[i].expiresAt < then) + then = eventLoop.timeouts[i].expiresAt; + } + + /* Calculate how long we should wait for a timeout if needed */ + if (then > 0) { + struct timeval tv; + + if (gettimeofday(&tv, NULL) < 0) { + return -1; + } + + *timeout = then - + ((((unsigned long long)tv.tv_sec)*1000) + + (((unsigned long long)tv.tv_usec)/1000)); + + if (*timeout < 0) + *timeout = 0; + } else { + *timeout = -1; + } + + EVENT_DEBUG("Timeout at %llu due in %d ms", then, *timeout); + + return 0; +} + +/* + * Allocate a pollfd array containing data for all registered + * file handles. The caller must free the returned data struct + * returns: the pollfd array, or NULL on error + */ +static struct pollfd *virEventPollMakePollFDs(int *nfds) { + struct pollfd *fds; + int i; + + *nfds = 0; + for (i = 0 ; i < eventLoop.handlesCount ; i++) { + if (eventLoop.handles[i].events) + (*nfds)++; + } + + /* Setup the poll file handle data structs */ + if (VIR_ALLOC_N(fds, *nfds) < 0) + return NULL; + + *nfds = 0; + for (i = 0 ; i < eventLoop.handlesCount ; i++) { + EVENT_DEBUG("Prepare n=%d w=%d, f=%d e=%d", i, + eventLoop.handles[i].watch, + eventLoop.handles[i].fd, + eventLoop.handles[i].events); + if (!eventLoop.handles[i].events) + continue; + fds[*nfds].fd = eventLoop.handles[i].fd; + fds[*nfds].events = eventLoop.handles[i].events; + fds[*nfds].revents = 0; + (*nfds)++; + //EVENT_DEBUG("Wait for %d %d", eventLoop.handles[i].fd, eventLoop.handles[i].events); + } + + return fds; +} + + +/* + * Iterate over all timers and determine if any have expired. + * Invoke the user supplied callback for each timer whose + * expiry time is met, and schedule the next timeout. Does + * not try to 'catch up' on time if the actual expiry time + * was later than the requested time. + * + * This method must cope with new timers being registered + * by a callback, and must skip any timers marked as deleted. + * + * Returns 0 upon success, -1 if an error occurred + */ +static int virEventPollDispatchTimeouts(void) { + struct timeval tv; + unsigned long long now; + int i; + /* Save this now - it may be changed during dispatch */ + int ntimeouts = eventLoop.timeoutsCount; + VIR_DEBUG("Dispatch %d", ntimeouts); + + if (gettimeofday(&tv, NULL) < 0) { + return -1; + } + now = (((unsigned long long)tv.tv_sec)*1000) + + (((unsigned long long)tv.tv_usec)/1000); + + for (i = 0 ; i < ntimeouts ; i++) { + if (eventLoop.timeouts[i].deleted || eventLoop.timeouts[i].frequency < 0) + continue; + + /* Add 20ms fuzz so we don't pointlessly spin doing + * <10ms sleeps, particularly on kernels with low HZ + * it is fine that a timer expires 20ms earlier than + * requested + */ + if (eventLoop.timeouts[i].expiresAt <= (now+20)) { + virEventTimeoutCallback cb = eventLoop.timeouts[i].cb; + int timer = eventLoop.timeouts[i].timer; + void *opaque = eventLoop.timeouts[i].opaque; + eventLoop.timeouts[i].expiresAt = + now + eventLoop.timeouts[i].frequency; + + virMutexUnlock(&eventLoop.lock); + (cb)(timer, opaque); + virMutexLock(&eventLoop.lock); + } + } + return 0; +} + + +/* Iterate over all file handles and dispatch any which + * have pending events listed in the poll() data. Invoke + * the user supplied callback for each handle which has + * pending events + * + * This method must cope with new handles being registered + * by a callback, and must skip any handles marked as deleted. + * + * Returns 0 upon success, -1 if an error occurred + */ +static int virEventPollDispatchHandles(int nfds, struct pollfd *fds) { + int i, n; + VIR_DEBUG("Dispatch %d", nfds); + + /* NB, use nfds not eventLoop.handlesCount, because new + * fds might be added on end of list, and they're not + * in the fds array we've got */ + for (i = 0, n = 0 ; n < nfds && i < eventLoop.handlesCount ; n++) { + while ((eventLoop.handles[i].fd != fds[n].fd || + eventLoop.handles[i].events == 0) && + i < eventLoop.handlesCount) { + i++; + } + if (i == eventLoop.handlesCount) + break; + + VIR_DEBUG("i=%d w=%d", i, eventLoop.handles[i].watch); + if (eventLoop.handles[i].deleted) { + EVENT_DEBUG("Skip deleted n=%d w=%d f=%d", i, + eventLoop.handles[i].watch, eventLoop.handles[i].fd); + continue; + } + + if (fds[n].revents) { + virEventHandleCallback cb = eventLoop.handles[i].cb; + int watch = eventLoop.handles[i].watch; + void *opaque = eventLoop.handles[i].opaque; + int hEvents = virEventPollFromNativeEvents(fds[n].revents); + EVENT_DEBUG("Dispatch n=%d f=%d w=%d e=%d %p", i, + fds[n].fd, watch, fds[n].revents, opaque); + virMutexUnlock(&eventLoop.lock); + (cb)(watch, fds[n].fd, hEvents, opaque); + virMutexLock(&eventLoop.lock); + } + } + + return 0; +} + + +/* Used post dispatch to actually remove any timers that + * were previously marked as deleted. This asynchronous + * cleanup is needed to make dispatch re-entrant safe. + */ +static void virEventPollCleanupTimeouts(void) { + int i; + size_t gap; + VIR_DEBUG("Cleanup %zu", eventLoop.timeoutsCount); + + /* Remove deleted entries, shuffling down remaining + * entries as needed to form contiguous series + */ + for (i = 0 ; i < eventLoop.timeoutsCount ; ) { + if (!eventLoop.timeouts[i].deleted) { + i++; + continue; + } + + EVENT_DEBUG("Purging timeout %d with id %d", i, + eventLoop.timeouts[i].timer); + if (eventLoop.timeouts[i].ff) + (eventLoop.timeouts[i].ff)(eventLoop.timeouts[i].opaque); + + if ((i+1) < eventLoop.timeoutsCount) { + memmove(eventLoop.timeouts+i, + eventLoop.timeouts+i+1, + sizeof(struct virEventPollTimeout)*(eventLoop.timeoutsCount + -(i+1))); + } + eventLoop.timeoutsCount--; + } + + /* Release some memory if we've got a big chunk free */ + gap = eventLoop.timeoutsAlloc - eventLoop.timeoutsCount; + if (eventLoop.timeoutsCount == 0 || + (gap > eventLoop.timeoutsCount && gap > EVENT_ALLOC_EXTENT)) { + EVENT_DEBUG("Found %zu out of %zu timeout slots used, releasing %zu", + eventLoop.timeoutsCount, eventLoop.timeoutsAlloc, gap); + VIR_SHRINK_N(eventLoop.timeouts, eventLoop.timeoutsAlloc, gap); + } +} + +/* Used post dispatch to actually remove any handles that + * were previously marked as deleted. This asynchronous + * cleanup is needed to make dispatch re-entrant safe. + */ +static void virEventPollCleanupHandles(void) { + int i; + size_t gap; + VIR_DEBUG("Cleanup %zu", eventLoop.handlesCount); + + /* Remove deleted entries, shuffling down remaining + * entries as needed to form contiguous series + */ + for (i = 0 ; i < eventLoop.handlesCount ; ) { + if (!eventLoop.handles[i].deleted) { + i++; + continue; + } + + if (eventLoop.handles[i].ff) + (eventLoop.handles[i].ff)(eventLoop.handles[i].opaque); + + if ((i+1) < eventLoop.handlesCount) { + memmove(eventLoop.handles+i, + eventLoop.handles+i+1, + sizeof(struct virEventPollHandle)*(eventLoop.handlesCount + -(i+1))); + } + eventLoop.handlesCount--; + } + + /* Release some memory if we've got a big chunk free */ + gap = eventLoop.handlesAlloc - eventLoop.handlesCount; + if (eventLoop.handlesCount == 0 || + (gap > eventLoop.handlesCount && gap > EVENT_ALLOC_EXTENT)) { + EVENT_DEBUG("Found %zu out of %zu handles slots used, releasing %zu", + eventLoop.handlesCount, eventLoop.handlesAlloc, gap); + VIR_SHRINK_N(eventLoop.handles, eventLoop.handlesAlloc, gap); + } +} + +/* + * Run a single iteration of the event loop, blocking until + * at least one file handle has an event, or a timer expires + */ +int virEventPollRunOnce(void) { + struct pollfd *fds = NULL; + int ret, timeout, nfds; + + virMutexLock(&eventLoop.lock); + eventLoop.running = 1; + virThreadSelf(&eventLoop.leader); + + virEventPollCleanupTimeouts(); + virEventPollCleanupHandles(); + + if (!(fds = virEventPollMakePollFDs(&nfds)) || + virEventPollCalculateTimeout(&timeout) < 0) + goto error; + + virMutexUnlock(&eventLoop.lock); + + retry: + EVENT_DEBUG("Poll on %d handles %p timeout %d", nfds, fds, timeout); + ret = poll(fds, nfds, timeout); + if (ret < 0) { + EVENT_DEBUG("Poll got error event %d", errno); + if (errno == EINTR) { + goto retry; + } + goto error_unlocked; + } + EVENT_DEBUG("Poll got %d event(s)", ret); + + virMutexLock(&eventLoop.lock); + if (virEventPollDispatchTimeouts() < 0) + goto error; + + if (ret > 0 && + virEventPollDispatchHandles(nfds, fds) < 0) + goto error; + + virEventPollCleanupTimeouts(); + virEventPollCleanupHandles(); + + eventLoop.running = 0; + virMutexUnlock(&eventLoop.lock); + VIR_FREE(fds); + return 0; + +error: + virMutexUnlock(&eventLoop.lock); +error_unlocked: + VIR_FREE(fds); + return -1; +} + + +static void virEventPollHandleWakeup(int watch ATTRIBUTE_UNUSED, + int fd, + int events ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) +{ + char c; + virMutexLock(&eventLoop.lock); + ignore_value(saferead(fd, &c, sizeof(c))); + virMutexUnlock(&eventLoop.lock); +} + +int virEventPollInit(void) +{ + if (virMutexInit(&eventLoop.lock) < 0) { + return -1; + } + + if (pipe(eventLoop.wakeupfd) < 0 || + virSetNonBlock(eventLoop.wakeupfd[0]) < 0 || + virSetNonBlock(eventLoop.wakeupfd[1]) < 0 || + virSetCloseExec(eventLoop.wakeupfd[0]) < 0 || + virSetCloseExec(eventLoop.wakeupfd[1]) < 0) { + return -1; + } + + if (virEventPollAddHandle(eventLoop.wakeupfd[0], + VIR_EVENT_HANDLE_READABLE, + virEventPollHandleWakeup, NULL, NULL) < 0) { + return -1; + } + + return 0; +} + +static int virEventPollInterruptLocked(void) +{ + char c = '\0'; + + if (!eventLoop.running || + virThreadIsSelf(&eventLoop.leader)) { + VIR_DEBUG("Skip interrupt, %d %d", eventLoop.running, + virThreadID(&eventLoop.leader)); + return 0; + } + + VIR_DEBUG0("Interrupting"); + if (safewrite(eventLoop.wakeupfd[1], &c, sizeof(c)) != sizeof(c)) + return -1; + return 0; +} + +int virEventPollInterrupt(void) +{ + int ret; + virMutexLock(&eventLoop.lock); + ret = virEventPollInterruptLocked(); + virMutexUnlock(&eventLoop.lock); + return ret; +} + +int +virEventPollToNativeEvents(int events) +{ + int ret = 0; + if(events & VIR_EVENT_HANDLE_READABLE) + ret |= POLLIN; + if(events & VIR_EVENT_HANDLE_WRITABLE) + ret |= POLLOUT; + if(events & VIR_EVENT_HANDLE_ERROR) + ret |= POLLERR; + if(events & VIR_EVENT_HANDLE_HANGUP) + ret |= POLLHUP; + return ret; +} + +int +virEventPollFromNativeEvents(int events) +{ + int ret = 0; + if(events & POLLIN) + ret |= VIR_EVENT_HANDLE_READABLE; + if(events & POLLOUT) + ret |= VIR_EVENT_HANDLE_WRITABLE; + if(events & POLLERR) + ret |= VIR_EVENT_HANDLE_ERROR; + if(events & POLLNVAL) /* Treat NVAL as error, since libvirt doesn't distinguish */ + ret |= VIR_EVENT_HANDLE_ERROR; + if(events & POLLHUP) + ret |= VIR_EVENT_HANDLE_HANGUP; + return ret; +} diff --git a/src/util/event_poll.h b/src/util/event_poll.h new file mode 100644 index 0000000..4ab3789 --- /dev/null +++ b/src/util/event_poll.h @@ -0,0 +1,132 @@ +/* + * event.h: event loop for monitoring file handles + * + * Copyright (C) 2007 Daniel P. Berrange + * Copyright (C) 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_EVENT_POLL_H__ +# define __VIR_EVENT_POLL_H__ + +# include "internal.h" + +/** + * virEventPollAddHandle: register a callback for monitoring file handle events + * + * @fd: file handle to monitor for events + * @events: bitset of events to watch from POLLnnn constants + * @cb: callback to invoke when an event occurs + * @opaque: user data to pass to callback + * + * returns -1 if the file handle cannot be registered, 0 upon success + */ +int virEventPollAddHandle(int fd, int events, + virEventHandleCallback cb, + void *opaque, + virFreeCallback ff); + +/** + * virEventPollUpdateHandle: change event set for a monitored file handle + * + * @watch: watch whose handle to update + * @events: bitset of events to watch from POLLnnn constants + * + * Will not fail if fd exists + */ +void virEventPollUpdateHandle(int watch, int events); + +/** + * virEventPollRemoveHandle: unregister a callback from a file handle + * + * @watch: watch whose handle to remove + * + * returns -1 if the file handle was not registered, 0 upon success + */ +int virEventPollRemoveHandle(int watch); + +/** + * virEventPollAddTimeout: register a callback for a timer event + * + * @frequency: time between events in milliseconds + * @cb: callback to invoke when an event occurs + * @opaque: user data to pass to callback + * + * Setting frequency to -1 will disable the timer. Setting the frequency + * to zero will cause it to fire on every event loop iteration. + * + * returns -1 if the file handle cannot be registered, a positive + * integer timer id upon success + */ +int virEventPollAddTimeout(int frequency, + virEventTimeoutCallback cb, + void *opaque, + virFreeCallback ff); + +/** + * virEventPollUpdateTimeout: change frequency for a timer + * + * @timer: timer id to change + * @frequency: time between events in milliseconds + * + * Setting frequency to -1 will disable the timer. Setting the frequency + * to zero will cause it to fire on every event loop iteration. + * + * Will not fail if timer exists + */ +void virEventPollUpdateTimeout(int timer, int frequency); + +/** + * virEventPollRemoveTimeout: unregister a callback for a timer + * + * @timer: the timer id to remove + * + * returns -1 if the timer was not registered, 0 upon success + */ +int virEventPollRemoveTimeout(int timer); + +/** + * virEventPollInit: Initialize the event loop + * + * returns -1 if initialization failed + */ +int virEventPollInit(void); + +/** + * virEventPollRunOnce: run a single iteration of the event loop. + * + * Blocks the caller until at least one file handle has an + * event or the first timer expires. + * + * returns -1 if the event monitoring failed + */ +int virEventPollRunOnce(void); + +int virEventPollFromNativeEvents(int events); +int virEventPollToNativeEvents(int events); + + +/** + * virEventPollInterrupt: wakeup any thread waiting in poll() + * + * return -1 if wakup failed + */ +int virEventPollInterrupt(void); + + +#endif /* __VIRTD_EVENT_H__ */ diff --git a/tools/Makefile.am b/tools/Makefile.am index 3dc549e..68471ea 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -38,7 +38,6 @@ virt-pki-validate.1: virt-pki-validate.in virsh_SOURCES = \ console.c console.h \ - ../daemon/event.c ../daemon/event.h \ virsh.c virsh_LDFLAGS = $(WARN_LDFLAGS) $(COVERAGE_LDFLAGS) diff --git a/tools/console.c b/tools/console.c index 48a469e..84c28a3 100644 --- a/tools/console.c +++ b/tools/console.c @@ -43,8 +43,8 @@ # include "memory.h" # include "virterror_internal.h" -# include "daemon/event.h" -# include "src/util/event.h" +# include "event.h" +# include "event_poll.h" /* ie Ctrl-] as per telnet */ # define CTRL_CLOSE_BRACKET '\35' @@ -350,7 +350,7 @@ int vshRunConsole(virDomainPtr dom, const char *devname) NULL); while (!con->quit) { - if (virEventRunOnce() < 0) + if (virEventPollRunOnce() < 0) break; } diff --git a/tools/virsh.c b/tools/virsh.c index 62fca17..f7ad14d 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -53,7 +53,7 @@ #include "xml.h" #include "libvirt/libvirt-qemu.h" #include "files.h" -#include "../daemon/event.h" +#include "event_poll.h" #include "configmake.h" #include "threads.h" #include "command.h" @@ -11672,13 +11672,13 @@ vshInit(vshControl *ctl) /* set up the signals handlers to catch disconnections */ vshSetupSignals(); - virEventRegisterImpl(virEventAddHandleImpl, - virEventUpdateHandleImpl, - virEventRemoveHandleImpl, - virEventAddTimeoutImpl, - virEventUpdateTimeoutImpl, - virEventRemoveTimeoutImpl); - virEventInit(); + virEventRegisterImpl(virEventPollAddHandle, + virEventPollUpdateHandle, + virEventPollRemoveHandle, + virEventPollAddTimeout, + virEventPollUpdateTimeout, + virEventPollRemoveTimeout); + virEventPollInit(); ctl->conn = virConnectOpenAuth(ctl->name, virConnectAuthPtrDefault, -- 1.7.4

On 03/03/2011 07:21 AM, Daniel P. Berrange wrote:
The event loop implementation is used by more than just the daemon, so move it into the shared area.
* daemon/event.c, src/util/event_poll.c: Renamed * daemon/event.h, src/util/event_poll.h: Renamed * tools/Makefile.am, tools/console.c, tools/virsh.c: Update to use new virEventPoll APIs * daemon/mdns.c, daemon/mdns.c, daemon/Makefile.am: Update to use new virEventPoll APIs --- daemon/Makefile.am | 1 - daemon/event.c | 706 ---------------------------------------------- daemon/event.h | 134 --------- daemon/libvirtd.c | 18 +- daemon/mdns.c | 6 +- src/Makefile.am | 1 + src/libvirt_private.syms | 13 + src/util/event_poll.c | 705 +++++++++++++++++++++++++++++++++++++++++++++ src/util/event_poll.h | 132 +++++++++ tools/Makefile.am | 1 - tools/console.c | 6 +- tools/virsh.c | 16 +- 12 files changed, 874 insertions(+), 865 deletions(-) delete mode 100644 daemon/event.c delete mode 100644 daemon/event.h create mode 100644 src/util/event_poll.c create mode 100644 src/util/event_poll.h
git config diff.rename true That cuts your 66k message down to 25k, making it easier to review. ACK if you squash this in (did you forget to run 'make check'?): diff --git i/tests/Makefile.am w/tests/Makefile.am index 5922b64..5896442 100644 --- i/tests/Makefile.am +++ w/tests/Makefile.am @@ -375,7 +375,7 @@ virbuftest_LDADD = $(LDADDS) if WITH_LIBVIRTD eventtest_SOURCES = \ - eventtest.c testutils.h testutils.c ../daemon/event.c + eventtest.c testutils.h testutils.c eventtest_LDADD = -lrt $(LDADDS) endif diff --git i/tests/eventtest.c w/tests/eventtest.c index 93317be..4c4a823 100644 --- i/tests/eventtest.c +++ w/tests/eventtest.c @@ -31,7 +31,7 @@ #include "threads.h" #include "logging.h" #include "util.h" -#include "../daemon/event.h" +#include "event_poll.h" #define NUM_FDS 31 #define NUM_TIME 31 @@ -89,7 +89,7 @@ testPipeReader(int watch, int fd, int events, void *data) info->error = EV_ERROR_NONE; if (info->delete != -1) - virEventRemoveHandleImpl(info->delete); + virEventPollRemoveHandle(info->delete); } @@ -108,7 +108,7 @@ testTimer(int timer, void *data) info->error = EV_ERROR_NONE; if (info->delete != -1) - virEventRemoveTimeoutImpl(info->delete); + virEventPollRemoveTimeout(info->delete); } static pthread_mutex_t eventThreadMutex = PTHREAD_MUTEX_INITIALIZER; @@ -127,7 +127,7 @@ static void *eventThreadLoop(void *data ATTRIBUTE_UNUSED) { eventThreadRunOnce = 0; pthread_mutex_unlock(&eventThreadMutex); - virEventRunOnce(); + virEventPollRunOnce(); pthread_mutex_lock(&eventThreadMutex); eventThreadJobDone = 1; @@ -288,12 +288,12 @@ mymain(int argc, char **argv) return EXIT_FAILURE; } - virEventInit(); + virEventPollInit(); for (i = 0 ; i < NUM_FDS ; i++) { handles[i].delete = -1; handles[i].watch = - virEventAddHandleImpl(handles[i].pipeFD[0], + virEventPollAddHandle(handles[i].pipeFD[0], VIR_EVENT_HANDLE_READABLE, testPipeReader, &handles[i], NULL); @@ -303,7 +303,7 @@ mymain(int argc, char **argv) timers[i].delete = -1; timers[i].timeout = -1; timers[i].timer = - virEventAddTimeoutImpl(timers[i].timeout, + virEventPollAddTimeout(timers[i].timeout, testTimer, &timers[i], NULL); } @@ -324,7 +324,7 @@ mymain(int argc, char **argv) /* Now lets delete one before starting poll(), and * try triggering another handle */ - virEventRemoveHandleImpl(handles[0].watch); + virEventPollRemoveHandle(handles[0].watch); startJob(); if (safewrite(handles[1].pipeFD[1], &one, 1) != 1) return EXIT_FAILURE; @@ -338,13 +338,13 @@ mymain(int argc, char **argv) /* NB: this case is subject to a bit of a race condition. * We yield & sleep, and pray that the other thread gets - * scheduled before we run EventRemoveHandleImpl */ + * scheduled before we run EventPollRemoveHandle */ startJob(); pthread_mutex_unlock(&eventThreadMutex); sched_yield(); usleep(100 * 1000); pthread_mutex_lock(&eventThreadMutex); - virEventRemoveHandleImpl(handles[1].watch); + virEventPollRemoveHandle(handles[1].watch); if (finishJob("Interrupted during poll", -1, -1) != EXIT_SUCCESS) return EXIT_FAILURE; @@ -380,22 +380,22 @@ mymain(int argc, char **argv) /* Run a timer on its own */ - virEventUpdateTimeoutImpl(timers[1].timer, 100); + virEventPollUpdateTimeout(timers[1].timer, 100); startJob(); if (finishJob("Firing a timer", -1, 1) != EXIT_SUCCESS) return EXIT_FAILURE; - virEventUpdateTimeoutImpl(timers[1].timer, -1); + virEventPollUpdateTimeout(timers[1].timer, -1); resetAll(); /* Now lets delete one before starting poll(), and * try triggering another timer */ - virEventUpdateTimeoutImpl(timers[1].timer, 100); - virEventRemoveTimeoutImpl(timers[0].timer); + virEventPollUpdateTimeout(timers[1].timer, 100); + virEventPollRemoveTimeout(timers[0].timer); startJob(); if (finishJob("Deleted before poll", -1, 1) != EXIT_SUCCESS) return EXIT_FAILURE; - virEventUpdateTimeoutImpl(timers[1].timer, -1); + virEventPollUpdateTimeout(timers[1].timer, -1); resetAll(); @@ -404,13 +404,13 @@ mymain(int argc, char **argv) /* NB: this case is subject to a bit of a race condition. * We yield & sleep, and pray that the other thread gets - * scheduled before we run EventRemoveTimeoutImpl */ + * scheduled before we run EventPollRemoveTimeout */ startJob(); pthread_mutex_unlock(&eventThreadMutex); sched_yield(); usleep(100 * 1000); pthread_mutex_lock(&eventThreadMutex); - virEventRemoveTimeoutImpl(timers[1].timer); + virEventPollRemoveTimeout(timers[1].timer); if (finishJob("Interrupted during poll", -1, -1) != EXIT_SUCCESS) return EXIT_FAILURE; @@ -423,27 +423,27 @@ mymain(int argc, char **argv) * before poll() exits for the first safewrite(). We don't * see a hard failure in other cases, so nothing to worry * about */ - virEventUpdateTimeoutImpl(timers[2].timer, 100); - virEventUpdateTimeoutImpl(timers[3].timer, 100); + virEventPollUpdateTimeout(timers[2].timer, 100); + virEventPollUpdateTimeout(timers[3].timer, 100); startJob(); timers[2].delete = timers[3].timer; if (finishJob("Deleted during dispatch", -1, 2) != EXIT_SUCCESS) return EXIT_FAILURE; - virEventUpdateTimeoutImpl(timers[2].timer, -1); + virEventPollUpdateTimeout(timers[2].timer, -1); resetAll(); /* Extreme fun, lets delete ourselves during dispatch */ - virEventUpdateTimeoutImpl(timers[2].timer, 100); + virEventPollUpdateTimeout(timers[2].timer, 100); startJob(); timers[2].delete = timers[2].timer; if (finishJob("Deleted during dispatch", -1, 2) != EXIT_SUCCESS) return EXIT_FAILURE; for (i = 0 ; i < NUM_FDS - 1 ; i++) - virEventRemoveHandleImpl(handles[i].watch); + virEventPollRemoveHandle(handles[i].watch); for (i = 0 ; i < NUM_TIME - 1 ; i++) - virEventRemoveTimeoutImpl(timers[i].timer); + virEventPollRemoveTimeout(timers[i].timer); resetAll(); @@ -464,11 +464,11 @@ mymain(int argc, char **argv) handles[0].pipeFD[0] = handles[1].pipeFD[0]; handles[0].pipeFD[1] = handles[1].pipeFD[1]; - handles[0].watch = virEventAddHandleImpl(handles[0].pipeFD[0], + handles[0].watch = virEventPollAddHandle(handles[0].pipeFD[0], 0, testPipeReader, &handles[0], NULL); - handles[1].watch = virEventAddHandleImpl(handles[1].pipeFD[0], + handles[1].watch = virEventPollAddHandle(handles[1].pipeFD[0], VIR_EVENT_HANDLE_READABLE, testPipeReader, &handles[1], NULL); -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Thu, Mar 03, 2011 at 04:01:35PM -0700, Eric Blake wrote:
On 03/03/2011 07:21 AM, Daniel P. Berrange wrote:
The event loop implementation is used by more than just the daemon, so move it into the shared area.
* daemon/event.c, src/util/event_poll.c: Renamed * daemon/event.h, src/util/event_poll.h: Renamed * tools/Makefile.am, tools/console.c, tools/virsh.c: Update to use new virEventPoll APIs * daemon/mdns.c, daemon/mdns.c, daemon/Makefile.am: Update to use new virEventPoll APIs --- daemon/Makefile.am | 1 - daemon/event.c | 706 ---------------------------------------------- daemon/event.h | 134 --------- daemon/libvirtd.c | 18 +- daemon/mdns.c | 6 +- src/Makefile.am | 1 + src/libvirt_private.syms | 13 + src/util/event_poll.c | 705 +++++++++++++++++++++++++++++++++++++++++++++ src/util/event_poll.h | 132 +++++++++ tools/Makefile.am | 1 - tools/console.c | 6 +- tools/virsh.c | 16 +- 12 files changed, 874 insertions(+), 865 deletions(-) delete mode 100644 daemon/event.c delete mode 100644 daemon/event.h create mode 100644 src/util/event_poll.c create mode 100644 src/util/event_poll.h
git config diff.rename true
That cuts your 66k message down to 25k, making it easier to review.
ACK if you squash this in (did you forget to run 'make check'?):
Urgh yes. I find it really annoying that plain 'make' does not cause the test suite to be compiled anymore:-( It certainly used to, but we lost it at some point & I've not checked where yet. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Fri, Mar 04, 2011 at 10:20:33 +0000, Daniel P. Berrange wrote:
ACK if you squash this in (did you forget to run 'make check'?):
Urgh yes. I find it really annoying that plain 'make' does not cause the test suite to be compiled anymore:-( It certainly used to, but we lost it at some point & I've not checked where yet.
I remember there was an explicit patch to move test suite compilation to the check phase since it's not normally needed... Jirka

On 03/04/2011 09:30 AM, Jiri Denemark wrote:
On Fri, Mar 04, 2011 at 10:20:33 +0000, Daniel P. Berrange wrote:
ACK if you squash this in (did you forget to run 'make check'?):
Urgh yes. I find it really annoying that plain 'make' does not cause the test suite to be compiled anymore:-( It certainly used to, but we lost it at some point & I've not checked where yet.
I remember there was an explicit patch to move test suite compilation to the check phase since it's not normally needed...
Yep - commit 2bd24003b99, in Aug 2010. Is it worth a patch that would add a configure variable, such as ./configure --enable-test-builds, which can be used to always compile the tests for development, but leave test compilation optional for others that only want it built if they plan on running 'make check'? It would be a matter of setting up an automake variable, something like: tests = virshtest conftest ... if ENABLE_TEST_BUILDS noinst_PROGRAMS = $tests else check_PROGRAMS = $tests endif Or is it not worth the bother, where I can just be lazy and leave things the way they are. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Not all applications have an existing event loop they need to integrate with. Forcing them to implement the libvirt event loop integration APIs is an undue burden. This just exposes our simple poll() based implementation for apps to use. So instead of calling virEventRegister(....callbacks...) The app would call virEventRegisterDefaultImpl() And then have a thread somewhere calling static bool quit = false; .... while (!quit) virEventRunDefaultImpl() * daemon/libvirtd.c, tools/console.c, tools/virsh.c: Convert to public event loop APIs * include/libvirt/libvirt.h.in, src/libvirt_private.syms: Add virEventRegisterDefaultImpl and virEventRunDefaultImpl * src/util/event.c: Implement virEventRegisterDefaultImpl and virEventRunDefaultImpl using poll() event loop * src/util/event_poll.c: Add full error reporting * src/util/virterror.c, include/libvirt/virterror.h: Add VIR_FROM_EVENTS --- daemon/libvirtd.c | 12 +---- include/libvirt/libvirt.h.in | 3 + include/libvirt/virterror.h | 1 + src/libvirt_private.syms | 8 ---- src/libvirt_public.syms | 6 +++ src/util/event.c | 94 +++++++++++++++++++++++++++++++++++++++++- src/util/event_poll.c | 24 ++++++++++- src/util/virterror.c | 3 + tools/console.c | 3 +- tools/virsh.c | 9 +--- 10 files changed, 133 insertions(+), 30 deletions(-) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index fdc9180..8c35627 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -873,8 +873,7 @@ static struct qemud_server *qemudInitialize(void) { return NULL; } - if (virEventPollInit() < 0) { - VIR_ERROR0(_("Failed to initialize event system")); + if (virEventRegisterDefaultImpl() < 0) { virMutexDestroy(&server->lock); if (virCondDestroy(&server->job) < 0) {} @@ -937,13 +936,6 @@ static struct qemud_server *qemudInitialize(void) { # endif #endif - virEventRegisterImpl(virEventPollAddHandle, - virEventPollUpdateHandle, - virEventPollRemoveHandle, - virEventPollAddTimeout, - virEventPollUpdateTimeout, - virEventPollRemoveTimeout); - return server; } @@ -2263,7 +2255,7 @@ qemudDispatchServerEvent(int watch, int fd, int events, void *opaque) { static int qemudOneLoop(void) { sig_atomic_t errors; - if (virEventPollRunOnce() < 0) + if (virEventRunDefaultImpl() < 0) return -1; /* Check for any signal handling errors and log them. */ diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 5dfb752..618b350 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1768,6 +1768,9 @@ void virEventRegisterImpl(virEventAddHandleFunc addHandle, virEventUpdateTimeoutFunc updateTimeout, virEventRemoveTimeoutFunc removeTimeout); +int virEventRegisterDefaultImpl(void); +int virEventRunDefaultImpl(void); + /* * Secret manipulation API */ diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 5962dbf..6b8c789 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -79,6 +79,7 @@ typedef enum { VIR_FROM_SYSINFO = 37, /* Error from sysinfo/SMBIOS */ VIR_FROM_STREAMS = 38, /* Error from I/O streams */ VIR_FROM_VMWARE = 39, /* Error from VMware driver */ + VIR_FROM_EVENT = 40, /* Error from event loop impl */ } virErrorDomain; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 5c7f4df..56b5bdf 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -396,14 +396,6 @@ virEventUpdateTimeout; # event_poll.h virEventPollToNativeEvents; virEventPollFromNativeEvents; -virEventPollRunOnce; -virEventPollInit; -virEventPollRemoveTimeout; -virEventPollUpdateTimeout; -virEventPollAddTimeout; -virEventPollRemoveHandle; -virEventPollUpdateHandle; -virEventPollAddHandle; # fdstream.h diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 1a45be1..cca8d08 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -424,4 +424,10 @@ LIBVIRT_0.8.8 { virConnectGetSysinfo; } LIBVIRT_0.8.6; +LIBVIRT_0.9.0 { + global: + virEventRegisterDefaultImpl; + virEventRunDefaultImpl; +} LIBVIRT_0.8.8; + # .... define new API here using predicted next version number .... diff --git a/src/util/event.c b/src/util/event.c index 680fef9..0d88b55 100644 --- a/src/util/event.c +++ b/src/util/event.c @@ -24,6 +24,9 @@ #include <config.h> #include "event.h" +#include "event_poll.h" +#include "logging.h" +#include "virterror_internal.h" #include <stdlib.h> @@ -77,6 +80,15 @@ int virEventRemoveTimeout(int timer) { return removeTimeoutImpl(timer); } + +/***************************************************** + * + * Below this point are 3 *PUBLIC* APIs for event + * loop integration with applications using libvirt. + * These API contracts cannot be changed. + * + *****************************************************/ + /** * virEventRegisterImpl: * @addHandle: the callback to add fd handles @@ -86,14 +98,28 @@ int virEventRemoveTimeout(int timer) { * @updateTimeout: the callback to update a timeout * @removeTimeout: the callback to remove a timeout * - * Registers an event implementation + * Registers an event implementation, to allow integration + * with an external event loop. Applications would use this + * to integrate with the libglib2 event loop, or libevent + * or the QT event loop. + * + * If an application does not need to integrate with an + * existing event loop implementation, then the + * virEventRegisterDefaultImpl method can be used to setup + * the generic libvirt implementation. */ void virEventRegisterImpl(virEventAddHandleFunc addHandle, virEventUpdateHandleFunc updateHandle, virEventRemoveHandleFunc removeHandle, virEventAddTimeoutFunc addTimeout, virEventUpdateTimeoutFunc updateTimeout, - virEventRemoveTimeoutFunc removeTimeout) { + virEventRemoveTimeoutFunc removeTimeout) +{ + VIR_DEBUG("addHandle=%p updateHandle=%p removeHandle=%p " + "addTimeout=%p updateTimeout=%p removeTimeout=%p", + addHandle, updateHandle, removeHandle, + addTimeout, updateTimeout, removeTimeout); + addHandleImpl = addHandle; updateHandleImpl = updateHandle; removeHandleImpl = removeHandle; @@ -101,3 +127,67 @@ void virEventRegisterImpl(virEventAddHandleFunc addHandle, updateTimeoutImpl = updateTimeout; removeTimeoutImpl = removeTimeout; } + +/** + * virEventRegisterDefaultImpl: + * + * Registers a default event implementation based on the + * poll() system call. This is a generic implementation + * that can be used by any client application which does + * not have a need to integrate with an external event + * loop impl. + * + * Once registered, the application can invoke + * virEventRunDefaultImpl in a loop to process + * events + */ +int virEventRegisterDefaultImpl(void) +{ + VIR_DEBUG0(""); + + virResetLastError(); + + if (virEventPollInit() < 0) { + virDispatchError(NULL); + return -1; + } + + virEventRegisterImpl( + virEventPollAddHandle, + virEventPollUpdateHandle, + virEventPollRemoveHandle, + virEventPollAddTimeout, + virEventPollUpdateTimeout, + virEventPollRemoveTimeout + ); + + return 0; +} + + +/** + * virEventRunDefaultImpl: + * + * Run one iteration of the event loop. Applications + * will generally want to have a thread which invokes + * this method in an infinite loop + * + * static bool quit = false; + * + * while (!quit) { + * if (virEventRunDefaultImpl() < 0) + * ...print error... + * } + */ +int virEventRunDefaultImpl(void) +{ + VIR_DEBUG0(""); + virResetLastError(); + + if (virEventPollRunOnce() < 0) { + virDispatchError(NULL); + return -1; + } + + return 0; +} diff --git a/src/util/event_poll.c b/src/util/event_poll.c index 1362840..dd83fc3 100644 --- a/src/util/event_poll.c +++ b/src/util/event_poll.c @@ -36,9 +36,16 @@ #include "memory.h" #include "util.h" #include "ignore-value.h" +#include "virterror_internal.h" #define EVENT_DEBUG(fmt, ...) VIR_DEBUG(fmt, __VA_ARGS__) +#define VIR_FROM_THIS VIR_FROM_EVENT + +#define virEventError(code, ...) \ + virReportErrorHelper(NULL, VIR_FROM_EVENT, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + static int virEventPollInterruptLocked(void); /* State for a single file handle being monitored */ @@ -316,6 +323,8 @@ static int virEventPollCalculateTimeout(int *timeout) { struct timeval tv; if (gettimeofday(&tv, NULL) < 0) { + virReportSystemError(errno, "%s", + _("Unable to get current time")); return -1; } @@ -350,8 +359,10 @@ static struct pollfd *virEventPollMakePollFDs(int *nfds) { } /* Setup the poll file handle data structs */ - if (VIR_ALLOC_N(fds, *nfds) < 0) + if (VIR_ALLOC_N(fds, *nfds) < 0) { + virReportOOMError(); return NULL; + } *nfds = 0; for (i = 0 ; i < eventLoop.handlesCount ; i++) { @@ -393,6 +404,8 @@ static int virEventPollDispatchTimeouts(void) { VIR_DEBUG("Dispatch %d", ntimeouts); if (gettimeofday(&tv, NULL) < 0) { + virReportSystemError(errno, "%s", + _("Unable to get current time")); return -1; } now = (((unsigned long long)tv.tv_sec)*1000) + @@ -584,6 +597,8 @@ int virEventPollRunOnce(void) { if (errno == EINTR) { goto retry; } + virReportSystemError(errno, "%s", + _("Unable to poll on file handles")); goto error_unlocked; } EVENT_DEBUG("Poll got %d event(s)", ret); @@ -626,6 +641,8 @@ static void virEventPollHandleWakeup(int watch ATTRIBUTE_UNUSED, int virEventPollInit(void) { if (virMutexInit(&eventLoop.lock) < 0) { + virReportSystemError(errno, "%s", + _("Unable to initialize mutex")); return -1; } @@ -634,12 +651,17 @@ int virEventPollInit(void) virSetNonBlock(eventLoop.wakeupfd[1]) < 0 || virSetCloseExec(eventLoop.wakeupfd[0]) < 0 || virSetCloseExec(eventLoop.wakeupfd[1]) < 0) { + virReportSystemError(errno, "%s", + _("Unable to setup wakeup pipe")); return -1; } if (virEventPollAddHandle(eventLoop.wakeupfd[0], VIR_EVENT_HANDLE_READABLE, virEventPollHandleWakeup, NULL, NULL) < 0) { + virEventError(VIR_ERR_INTERNAL_ERROR, + _("Unable to add handle %d to event loop"), + eventLoop.wakeupfd[0]); return -1; } diff --git a/src/util/virterror.c b/src/util/virterror.c index 89bb2a5..aaa3720 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -200,6 +200,9 @@ static const char *virErrorDomainName(virErrorDomain domain) { case VIR_FROM_STREAMS: dom = "Streams "; break; + case VIR_FROM_EVENT: + dom = "Events "; + break; } return(dom); } diff --git a/tools/console.c b/tools/console.c index 84c28a3..b9dd268 100644 --- a/tools/console.c +++ b/tools/console.c @@ -44,7 +44,6 @@ # include "virterror_internal.h" # include "event.h" -# include "event_poll.h" /* ie Ctrl-] as per telnet */ # define CTRL_CLOSE_BRACKET '\35' @@ -350,7 +349,7 @@ int vshRunConsole(virDomainPtr dom, const char *devname) NULL); while (!con->quit) { - if (virEventPollRunOnce() < 0) + if (virEventRunDefaultImpl() < 0) break; } diff --git a/tools/virsh.c b/tools/virsh.c index f7ad14d..2af307f 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -11672,13 +11672,8 @@ vshInit(vshControl *ctl) /* set up the signals handlers to catch disconnections */ vshSetupSignals(); - virEventRegisterImpl(virEventPollAddHandle, - virEventPollUpdateHandle, - virEventPollRemoveHandle, - virEventPollAddTimeout, - virEventPollUpdateTimeout, - virEventPollRemoveTimeout); - virEventPollInit(); + if (virEventRegisterDefaultImpl() < 0) + return FALSE; ctl->conn = virConnectOpenAuth(ctl->name, virConnectAuthPtrDefault, -- 1.7.4

On 03/03/2011 07:21 AM, Daniel P. Berrange wrote:
Not all applications have an existing event loop they need to integrate with. Forcing them to implement the libvirt event loop integration APIs is an undue burden. This just exposes our simple poll() based implementation for apps to use. So instead of calling
virEventRegister(....callbacks...)
The app would call
virEventRegisterDefaultImpl()
And then have a thread somewhere calling
static bool quit = false; .... while (!quit) virEventRunDefaultImpl()
* daemon/libvirtd.c, tools/console.c, tools/virsh.c: Convert to public event loop APIs * include/libvirt/libvirt.h.in, src/libvirt_private.syms: Add virEventRegisterDefaultImpl and virEventRunDefaultImpl * src/util/event.c: Implement virEventRegisterDefaultImpl and virEventRunDefaultImpl using poll() event loop * src/util/event_poll.c: Add full error reporting * src/util/virterror.c, include/libvirt/virterror.h: Add VIR_FROM_EVENTS --- daemon/libvirtd.c | 12 +---- include/libvirt/libvirt.h.in | 3 + include/libvirt/virterror.h | 1 + src/libvirt_private.syms | 8 ---- src/libvirt_public.syms | 6 +++ src/util/event.c | 94 +++++++++++++++++++++++++++++++++++++++++- src/util/event_poll.c | 24 ++++++++++- src/util/virterror.c | 3 + tools/console.c | 3 +- tools/virsh.c | 9 +--- 10 files changed, 133 insertions(+), 30 deletions(-)
You need to squash this in to keep 'make syntax-check' happy: diff --git i/po/POTFILES.in w/po/POTFILES.in index 9852f97..1ed2765 100644 --- i/po/POTFILES.in +++ w/po/POTFILES.in @@ -88,6 +88,7 @@ src/util/cgroup.c src/util/command.c src/util/conf.c src/util/dnsmasq.c +src/util/event_poll.c src/util/hooks.c src/util/hostusb.c src/util/interface.c
+++ b/include/libvirt/virterror.h @@ -79,6 +79,7 @@ typedef enum { VIR_FROM_SYSINFO = 37, /* Error from sysinfo/SMBIOS */ VIR_FROM_STREAMS = 38, /* Error from I/O streams */ VIR_FROM_VMWARE = 39, /* Error from VMware driver */ + VIR_FROM_EVENT = 40, /* Error from event loop impl */ } virErrorDomain;
Hmm, the line before had TAB, but your line has spaces, which makes it render odd in my reply. Preferences on which whitespace we should be using there? But any cleanup should be a separate patch.
+++ b/src/libvirt_private.syms @@ -396,14 +396,6 @@ virEventUpdateTimeout; # event_poll.h virEventPollToNativeEvents; virEventPollFromNativeEvents; -virEventPollRunOnce; -virEventPollInit; -virEventPollRemoveTimeout; -virEventPollUpdateTimeout; -virEventPollAddTimeout; -virEventPollRemoveHandle; -virEventPollUpdateHandle; -virEventPollAddHandle;
Nice - added in patch 2, and made private again in patch 3.
+++ b/src/libvirt_public.syms @@ -424,4 +424,10 @@ LIBVIRT_0.8.8 { virConnectGetSysinfo; } LIBVIRT_0.8.6;
+LIBVIRT_0.9.0 { + global: + virEventRegisterDefaultImpl; + virEventRunDefaultImpl; +} LIBVIRT_0.8.8;
So we're all in agreement that there's enough refactoring and other goodness going in to call the next version 0.9.0 :)
+ +/** + * virEventRegisterDefaultImpl: + * + * Registers a default event implementation based on the + * poll() system call. This is a generic implementation + * that can be used by any client application which does + * not have a need to integrate with an external event + * loop impl.
s/impl/implementation/ Abbreviations are fine in code and even method names, but in the documentation, it's nice to spell it out.
+ +/** + * virEventRunDefaultImpl: + * + * Run one iteration of the event loop. Applications + * will generally want to have a thread which invokes + * this method in an infinite loop
Maybe add a sentence: s/loop/loop. Each iteration of the loop blocks without consuming CPU until the next timeout or poll-based activity is detected./
+int virEventRunDefaultImpl(void) +{ + VIR_DEBUG0("");
Why ""? A timestamp in the log without contents looks suspicious; should we add some contents, such as "event loop started"? ACK with those nits addressed. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Thu, Mar 03, 2011 at 04:13:08PM -0700, Eric Blake wrote:
On 03/03/2011 07:21 AM, Daniel P. Berrange wrote:
Not all applications have an existing event loop they need to integrate with. Forcing them to implement the libvirt event loop integration APIs is an undue burden. This just exposes our simple poll() based implementation for apps to use. So instead of calling
virEventRegister(....callbacks...)
The app would call
virEventRegisterDefaultImpl()
And then have a thread somewhere calling
static bool quit = false; .... while (!quit) virEventRunDefaultImpl()
* daemon/libvirtd.c, tools/console.c, tools/virsh.c: Convert to public event loop APIs * include/libvirt/libvirt.h.in, src/libvirt_private.syms: Add virEventRegisterDefaultImpl and virEventRunDefaultImpl * src/util/event.c: Implement virEventRegisterDefaultImpl and virEventRunDefaultImpl using poll() event loop * src/util/event_poll.c: Add full error reporting * src/util/virterror.c, include/libvirt/virterror.h: Add VIR_FROM_EVENTS --- daemon/libvirtd.c | 12 +---- include/libvirt/libvirt.h.in | 3 + include/libvirt/virterror.h | 1 + src/libvirt_private.syms | 8 ---- src/libvirt_public.syms | 6 +++ src/util/event.c | 94 +++++++++++++++++++++++++++++++++++++++++- src/util/event_poll.c | 24 ++++++++++- src/util/virterror.c | 3 + tools/console.c | 3 +- tools/virsh.c | 9 +--- 10 files changed, 133 insertions(+), 30 deletions(-)
You need to squash this in to keep 'make syntax-check' happy:
diff --git i/po/POTFILES.in w/po/POTFILES.in index 9852f97..1ed2765 100644 --- i/po/POTFILES.in +++ w/po/POTFILES.in @@ -88,6 +88,7 @@ src/util/cgroup.c src/util/command.c src/util/conf.c src/util/dnsmasq.c +src/util/event_poll.c src/util/hooks.c src/util/hostusb.c src/util/interface.c
+++ b/include/libvirt/virterror.h @@ -79,6 +79,7 @@ typedef enum { VIR_FROM_SYSINFO = 37, /* Error from sysinfo/SMBIOS */ VIR_FROM_STREAMS = 38, /* Error from I/O streams */ VIR_FROM_VMWARE = 39, /* Error from VMware driver */ + VIR_FROM_EVENT = 40, /* Error from event loop impl */ } virErrorDomain;
Hmm, the line before had TAB, but your line has spaces, which makes it render odd in my reply. Preferences on which whitespace we should be using there? But any cleanup should be a separate patch.
Odd, I thought our make syntax-check blocked all use of TAB in our files.
+++ b/src/libvirt_public.syms @@ -424,4 +424,10 @@ LIBVIRT_0.8.8 { virConnectGetSysinfo; } LIBVIRT_0.8.6;
+LIBVIRT_0.9.0 { + global: + virEventRegisterDefaultImpl; + virEventRunDefaultImpl; +} LIBVIRT_0.8.8;
So we're all in agreement that there's enough refactoring and other goodness going in to call the next version 0.9.0 :)
There's far more to come from me too :-)
+int virEventRunDefaultImpl(void) +{ + VIR_DEBUG0("");
Why ""? A timestamp in the log without contents looks suspicious; should we add some contents, such as "event loop started"?
It isn't just the timestamp. The log will contain the function name, which is what I really want to see. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Fri, Mar 04, 2011 at 10:19:20AM +0000, Daniel P. Berrange wrote:
On Thu, Mar 03, 2011 at 04:13:08PM -0700, Eric Blake wrote:
On 03/03/2011 07:21 AM, Daniel P. Berrange wrote:
+LIBVIRT_0.9.0 { + global: + virEventRegisterDefaultImpl; + virEventRunDefaultImpl; +} LIBVIRT_0.8.8;
So we're all in agreement that there's enough refactoring and other goodness going in to call the next version 0.9.0 :)
There's far more to come from me too :-)
ACK on this, it's finally time to leave 0.8 land :-) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (4)
-
Daniel P. Berrange
-
Daniel Veillard
-
Eric Blake
-
Jiri Denemark