move daemon/event.* into src/util/ directory because
the timer needs the API virEventRunOnce().
git diff rename does not work...
Signed-off-by: Wen Congyang <wency(a)cn.fujitsu.com>
---
daemon/Makefile.am | 1 -
daemon/event.c | 700 ----------------------------------------------
daemon/event.h | 134 ---------
src/libvirt_private.syms | 11 +
src/util/event.c | 676 ++++++++++++++++++++++++++++++++++++++++++++-
src/util/event.h | 106 +++++++
tests/Makefile.am | 2 +-
tests/eventtest.c | 2 +-
tools/Makefile.am | 1 -
tools/console.c | 2 +-
tools/virsh.c | 2 +-
11 files changed, 795 insertions(+), 842 deletions(-)
delete mode 100644 daemon/event.c
delete mode 100644 daemon/event.h
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 3ffb7be..adab3cc 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 89ca9f0..0000000
--- a/daemon/event.c
+++ /dev/null
@@ -1,700 +0,0 @@
-/*
- * event.c: event loop for monitoring file handles
- *
- * Copyright (C) 2007, 2010 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(a)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, ...) 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;
- 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;
- 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;
-
- 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;
- 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, eventLoop.handles[i].watch,
- fds[n].revents, eventLoop.handles[i].opaque);
- virMutexUnlock(&eventLoop.lock);
- (cb)(eventLoop.handles[i].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;
- 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 */
- if ((eventLoop.timeoutsAlloc - EVENT_ALLOC_EXTENT) > eventLoop.timeoutsCount) {
- EVENT_DEBUG("Releasing %zu out of %zu timeout slots used, releasing
%d",
- eventLoop.timeoutsCount, eventLoop.timeoutsAlloc,
EVENT_ALLOC_EXTENT);
- VIR_SHRINK_N(eventLoop.timeouts, eventLoop.timeoutsAlloc,
- EVENT_ALLOC_EXTENT);
- }
- 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;
- 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 */
- if ((eventLoop.handlesAlloc - EVENT_ALLOC_EXTENT) > eventLoop.handlesCount) {
- EVENT_DEBUG("Releasing %zu out of %zu handles slots used, releasing
%d",
- eventLoop.handlesCount, eventLoop.handlesAlloc, EVENT_ALLOC_EXTENT);
- VIR_SHRINK_N(eventLoop.handles, eventLoop.handlesAlloc,
- EVENT_ALLOC_EXTENT);
- }
- 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(a)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/src/libvirt_private.syms b/src/libvirt_private.syms
index 19e581c..08570ae 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -370,11 +370,22 @@ ebtablesRemoveForwardAllowIn;
# event.h
virEventAddHandle;
+virEventAddHandleImpl;
virEventAddTimeout;
+virEventAddTimeoutImpl;
+virEventHandleTypeToPollEvent;
+virEventInit;
+virEventInterrupt;
virEventRemoveHandle;
+virEventRemoveHandleImpl;
virEventRemoveTimeout;
+virEventRemoveTimeoutImpl;
+virEventRunOnce;
virEventUpdateHandle;
+virEventUpdateHandleImpl;
virEventUpdateTimeout;
+virEventUpdateTimeoutImpl;
+virPollEventToEventHandleType;
# fdstream.h
diff --git a/src/util/event.c b/src/util/event.c
index 680fef9..4a4beb4 100644
--- a/src/util/event.c
+++ b/src/util/event.c
@@ -1,8 +1,8 @@
/*
* event.c: event loop for monitoring file handles
*
+ * Copyright (C) 2007, 2010 Red Hat, Inc.
* 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
@@ -23,9 +23,21 @@
#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"
-#include <stdlib.h>
+#define EVENT_DEBUG(fmt, ...) DEBUG(fmt, __VA_ARGS__)
static virEventAddHandleFunc addHandleImpl = NULL;
static virEventUpdateHandleFunc updateHandleImpl = NULL;
@@ -101,3 +113,663 @@ void virEventRegisterImpl(virEventAddHandleFunc addHandle,
updateTimeoutImpl = updateTimeout;
removeTimeoutImpl = removeTimeout;
}
+
+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;
+ 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;
+ 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;
+
+ 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;
+ 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, eventLoop.handles[i].watch,
+ fds[n].revents, eventLoop.handles[i].opaque);
+ virMutexUnlock(&eventLoop.lock);
+ (cb)(eventLoop.handles[i].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;
+ 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 */
+ if ((eventLoop.timeoutsAlloc - EVENT_ALLOC_EXTENT) > eventLoop.timeoutsCount) {
+ EVENT_DEBUG("Releasing %zu out of %zu timeout slots used, releasing
%d",
+ eventLoop.timeoutsCount, eventLoop.timeoutsAlloc,
EVENT_ALLOC_EXTENT);
+ VIR_SHRINK_N(eventLoop.timeouts, eventLoop.timeoutsAlloc,
+ EVENT_ALLOC_EXTENT);
+ }
+ 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;
+ 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 */
+ if ((eventLoop.handlesAlloc - EVENT_ALLOC_EXTENT) > eventLoop.handlesCount) {
+ EVENT_DEBUG("Releasing %zu out of %zu handles slots used, releasing
%d",
+ eventLoop.handlesCount, eventLoop.handlesAlloc, EVENT_ALLOC_EXTENT);
+ VIR_SHRINK_N(eventLoop.handles, eventLoop.handlesAlloc,
+ EVENT_ALLOC_EXTENT);
+ }
+ 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/src/util/event.h b/src/util/event.h
index 68b06c6..f59c125 100644
--- a/src/util/event.h
+++ b/src/util/event.h
@@ -23,6 +23,7 @@
#ifndef __VIR_EVENT_H__
# define __VIR_EVENT_H__
+
# include "internal.h"
/**
* virEventAddHandle: register a callback for monitoring file handle events
@@ -98,4 +99,109 @@ void virEventUpdateTimeout(int timer, int frequency);
*/
int virEventRemoveTimeout(int timer);
+/**
+ * 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 /* __VIR_EVENT_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 697b401..746cfa5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -402,7 +402,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 a/tests/eventtest.c b/tests/eventtest.c
index 067e365..6ab1fd4 100644
--- a/tests/eventtest.c
+++ b/tests/eventtest.c
@@ -31,7 +31,7 @@
#include "threads.h"
#include "logging.h"
#include "util.h"
-#include "../daemon/event.h"
+#include "event.h"
#define NUM_FDS 5
#define NUM_TIME 5
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 271c11b..4f56712 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -38,7 +38,6 @@ virt-pki-validate.1: virt-pki-validate
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 e126320..3f09c54 100644
--- a/tools/console.c
+++ b/tools/console.c
@@ -44,7 +44,7 @@
# include "memory.h"
# include "virterror_internal.h"
-# include "daemon/event.h"
+# include "event.h"
/* ie Ctrl-] as per telnet */
# define CTRL_CLOSE_BRACKET '\35'
diff --git a/tools/virsh.c b/tools/virsh.c
index 55e2a68..094870c 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -52,7 +52,7 @@
#include "xml.h"
#include "libvirt/libvirt-qemu.h"
#include "files.h"
-#include "../daemon/event.h"
+#include "event.h"
#include "configmake.h"
static char *progname;
--
1.7.1