[libvirt] [PATCH 2/3] Move event code out of the daemon/ into src/util/

Daniel P. Berrange berrange at redhat.com
Thu Mar 3 14:21:50 UTC 2011


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 at 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 at 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 at 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 at 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




More information about the libvir-list mailing list