diff -r d5dc15adcbea qemud/event.c --- a/qemud/event.c Fri May 08 11:58:19 2009 +0100 +++ b/qemud/event.c Fri May 08 16:09:05 2009 +0100 @@ -30,7 +30,8 @@ #include #include -#include "qemud.h" +#include "threads.h" +#include "logging.h" #include "event.h" #include "memory.h" #include "util.h" @@ -83,10 +84,10 @@ struct virEventLoop { static struct virEventLoop eventLoop; /* Unique ID for the next FD watch to be registered */ -static int nextWatch = 0; +static int nextWatch = 1; /* Unique ID for the next timer to be registered */ -static int nextTimer = 0; +static int nextTimer = 1; static void virEventLock(void) { @@ -142,15 +143,22 @@ int virEventAddHandleImpl(int fd, int ev 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; + } + virEventLock(); for (i = 0 ; i < eventLoop.handlesCount ; i++) { if (eventLoop.handles[i].watch == watch) { eventLoop.handles[i].events = virEventHandleTypeToPollEvent(events); + virEventInterruptLocked(); break; } } - virEventInterruptLocked(); virEventUnlock(); } @@ -163,6 +171,12 @@ void virEventUpdateHandleImpl(int watch, int virEventRemoveHandleImpl(int watch) { int i; EVENT_DEBUG("Remove handle %d", watch); + + if (watch <= 0) { + VIR_WARN("Ignoring invalid remove watch %d", watch); + return -1; + } + virEventLock(); for (i = 0 ; i < eventLoop.handlesCount ; i++) { if (eventLoop.handles[i].deleted) @@ -171,11 +185,11 @@ int virEventRemoveHandleImpl(int watch) if (eventLoop.handles[i].watch == watch) { EVENT_DEBUG("mark delete %d %d", i, eventLoop.handles[i].fd); eventLoop.handles[i].deleted = 1; + virEventInterruptLocked(); virEventUnlock(); return 0; } } - virEventInterruptLocked(); virEventUnlock(); return -1; } @@ -231,6 +245,12 @@ void virEventUpdateTimeoutImpl(int timer 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; } @@ -243,10 +263,10 @@ void virEventUpdateTimeoutImpl(int timer frequency >= 0 ? frequency + (((unsigned long long)tv.tv_sec)*1000) + (((unsigned long long)tv.tv_usec)/1000) : 0; + virEventInterruptLocked(); break; } } - virEventInterruptLocked(); virEventUnlock(); } @@ -259,6 +279,12 @@ void virEventUpdateTimeoutImpl(int timer 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; + } + virEventLock(); for (i = 0 ; i < eventLoop.timeoutsCount ; i++) { if (eventLoop.timeouts[i].deleted) @@ -266,11 +292,11 @@ int virEventRemoveTimeoutImpl(int timer) if (eventLoop.timeouts[i].timer == timer) { eventLoop.timeouts[i].deleted = 1; + virEventInterruptLocked(); virEventUnlock(); return 0; } } - virEventInterruptLocked(); virEventUnlock(); return -1; } @@ -287,7 +313,7 @@ static int virEventCalculateTimeout(int EVENT_DEBUG("Calculate expiry of %d timers", eventLoop.timeoutsCount); /* Figure out if we need a timeout */ for (i = 0 ; i < eventLoop.timeoutsCount ; i++) { - if (eventLoop.timeouts[i].deleted || eventLoop.timeouts[i].frequency < 0) + if (eventLoop.timeouts[i].frequency < 0) continue; EVENT_DEBUG("Got a timeout scheduled for %llu", eventLoop.timeouts[i].expiresAt); @@ -324,32 +350,26 @@ static int virEventCalculateTimeout(int * file handles. The caller must free the returned data struct * returns: the pollfd array, or NULL on error */ -static int virEventMakePollFDs(struct pollfd **retfds) { +static struct pollfd *virEventMakePollFDs(void) { struct pollfd *fds; - int i, nfds = 0; + int i; + + /* Setup the poll file handle data structs */ + if (VIR_ALLOC_N(fds, eventLoop.handlesCount) < 0) + return NULL; for (i = 0 ; i < eventLoop.handlesCount ; i++) { - if (eventLoop.handles[i].deleted) - continue; - nfds++; - } - *retfds = NULL; - /* Setup the poll file handle data structs */ - if (VIR_ALLOC_N(fds, nfds) < 0) - return -1; - - for (i = 0, nfds = 0 ; i < eventLoop.handlesCount ; i++) { - if (eventLoop.handles[i].deleted) - continue; - fds[nfds].fd = eventLoop.handles[i].fd; - fds[nfds].events = eventLoop.handles[i].events; - fds[nfds].revents = 0; + EVENT_DEBUG("Prepare n=%d w=%d, f=%d e=%d", i, + eventLoop.handles[i].watch, + eventLoop.handles[i].fd, + eventLoop.handles[i].events); + fds[i].fd = eventLoop.handles[i].fd; + fds[i].events = eventLoop.handles[i].events; + fds[i].revents = 0; //EVENT_DEBUG("Wait for %d %d", eventLoop.handles[i].fd, eventLoop.handles[i].events); - nfds++; } - *retfds = fds; - return nfds; + return fds; } @@ -409,26 +429,30 @@ static int virEventDispatchTimeouts(void * Returns 0 upon success, -1 if an error occurred */ static int virEventDispatchHandles(int nfds, struct pollfd *fds) { - int i, n; + int i; - for (i = 0, n = 0 ; i < eventLoop.handlesCount && n < nfds ; i++) { + /* 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 ; i < nfds ; i++) { if (eventLoop.handles[i].deleted) { - EVENT_DEBUG("Skip deleted %d", eventLoop.handles[i].fd); + EVENT_DEBUG("Skip deleted n=%d w=%d f=%d", i, + eventLoop.handles[i].watch, eventLoop.handles[i].fd); continue; } - if (fds[n].revents) { + if (fds[i].revents) { virEventHandleCallback cb = eventLoop.handles[i].cb; void *opaque = eventLoop.handles[i].opaque; - int hEvents = virPollEventToEventHandleType(fds[n].revents); - EVENT_DEBUG("Dispatch %d %d %p", fds[n].fd, - fds[n].revents, eventLoop.handles[i].opaque); + int hEvents = virPollEventToEventHandleType(fds[i].revents); + EVENT_DEBUG("Dispatch n=%d f=%d w=%d e=%d %p", i, + fds[i].fd, eventLoop.handles[i].watch, + fds[i].revents, eventLoop.handles[i].opaque); virEventUnlock(); (cb)(eventLoop.handles[i].watch, - fds[n].fd, hEvents, opaque); + fds[i].fd, hEvents, opaque); virEventLock(); } - n++; } return 0; @@ -519,22 +543,21 @@ static int virEventCleanupHandles(void) * at least one file handle has an event, or a timer expires */ int virEventRunOnce(void) { - struct pollfd *fds; + struct pollfd *fds = NULL; int ret, timeout, nfds; virEventLock(); eventLoop.running = 1; eventLoop.leader = pthread_self(); - if ((nfds = virEventMakePollFDs(&fds)) < 0) { - virEventUnlock(); - return -1; - } - if (virEventCalculateTimeout(&timeout) < 0) { - VIR_FREE(fds); - virEventUnlock(); - return -1; - } + if (virEventCleanupTimeouts() < 0 || + virEventCleanupHandles() < 0) + goto error; + + if (!(fds = virEventMakePollFDs()) || + virEventCalculateTimeout(&timeout) < 0) + goto error; + nfds = eventLoop.handlesCount; virEventUnlock(); @@ -546,38 +569,31 @@ int virEventRunOnce(void) { if (errno == EINTR) { goto retry; } - VIR_FREE(fds); - return -1; + goto error_unlocked; } virEventLock(); - if (virEventDispatchTimeouts() < 0) { - VIR_FREE(fds); - virEventUnlock(); - return -1; - } + if (virEventDispatchTimeouts() < 0) + goto error; if (ret > 0 && - virEventDispatchHandles(nfds, fds) < 0) { - VIR_FREE(fds); - virEventUnlock(); - return -1; - } - VIR_FREE(fds); + virEventDispatchHandles(nfds, fds) < 0) + goto error; - if (virEventCleanupTimeouts() < 0) { - virEventUnlock(); - return -1; - } - - if (virEventCleanupHandles() < 0) { - virEventUnlock(); - return -1; - } + if (virEventCleanupTimeouts() < 0 || + virEventCleanupHandles() < 0) + goto error; eventLoop.running = 0; virEventUnlock(); + VIR_FREE(fds); return 0; + +error: + virEventUnlock(); +error_unlocked: + VIR_FREE(fds); + return -1; } static void virEventHandleWakeup(int watch ATTRIBUTE_UNUSED, @@ -597,10 +613,10 @@ int virEventInit(void) return -1; if (pipe(eventLoop.wakeupfd) < 0 || - qemudSetNonBlock(eventLoop.wakeupfd[0]) < 0 || - qemudSetNonBlock(eventLoop.wakeupfd[1]) < 0 || - qemudSetCloseExec(eventLoop.wakeupfd[0]) < 0 || - qemudSetCloseExec(eventLoop.wakeupfd[1]) < 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], @@ -616,9 +632,12 @@ static int virEventInterruptLocked(void) char c = '\0'; if (!eventLoop.running || - pthread_self() == eventLoop.leader) + pthread_self() == eventLoop.leader) { + VIR_DEBUG("Skip interrupt, %d %d", eventLoop.running, (int)eventLoop.leader); return 0; + } + VIR_DEBUG0("Interrupting"); if (safewrite(eventLoop.wakeupfd[1], &c, sizeof(c)) != sizeof(c)) return -1; return 0; diff -r d5dc15adcbea qemud/qemud.c --- a/qemud/qemud.c Fri May 08 11:58:19 2009 +0100 +++ b/qemud/qemud.c Fri May 08 16:09:05 2009 +0100 @@ -371,32 +371,6 @@ qemudDispatchSignalEvent(int watch ATTRI virMutexUnlock(&server->lock); } -int qemudSetCloseExec(int fd) { - int flags; - if ((flags = fcntl(fd, F_GETFD)) < 0) - goto error; - flags |= FD_CLOEXEC; - if ((fcntl(fd, F_SETFD, flags)) < 0) - goto error; - return 0; - error: - VIR_ERROR0(_("Failed to set close-on-exec file descriptor flag")); - return -1; -} - - -int qemudSetNonBlock(int fd) { - int flags; - if ((flags = fcntl(fd, F_GETFL)) < 0) - goto error; - flags |= O_NONBLOCK; - if ((fcntl(fd, F_SETFL, flags)) < 0) - goto error; - return 0; - error: - VIR_ERROR0(_("Failed to set non-blocking file descriptor flag")); - return -1; -} static int qemudGoDaemon(void) { int pid = fork(); @@ -525,8 +499,8 @@ static int qemudListenUnix(struct qemud_ goto cleanup; } - if (qemudSetCloseExec(sock->fd) < 0 || - qemudSetNonBlock(sock->fd) < 0) + if (virSetCloseExec(sock->fd) < 0 || + virSetNonBlock(sock->fd) < 0) goto cleanup; memset(&addr, 0, sizeof(addr)); @@ -687,8 +661,8 @@ remoteListenTCP (struct qemud_server *se else sock->port = -1; - if (qemudSetCloseExec(sock->fd) < 0 || - qemudSetNonBlock(sock->fd) < 0) + if (virSetCloseExec(sock->fd) < 0 || + virSetNonBlock(sock->fd) < 0) goto cleanup; if (listen (sock->fd, 30) < 0) { @@ -1273,8 +1247,8 @@ static int qemudDispatchServer(struct qe setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&no_slow_start, sizeof no_slow_start); - if (qemudSetCloseExec(fd) < 0 || - qemudSetNonBlock(fd) < 0) { + if (virSetCloseExec(fd) < 0 || + virSetNonBlock(fd) < 0) { close(fd); return -1; } @@ -2872,10 +2846,10 @@ int main(int argc, char **argv) { goto error1; if (pipe(sigpipe) < 0 || - qemudSetNonBlock(sigpipe[0]) < 0 || - qemudSetNonBlock(sigpipe[1]) < 0 || - qemudSetCloseExec(sigpipe[0]) < 0 || - qemudSetCloseExec(sigpipe[1]) < 0) { + virSetNonBlock(sigpipe[0]) < 0 || + virSetNonBlock(sigpipe[1]) < 0 || + virSetCloseExec(sigpipe[0]) < 0 || + virSetCloseExec(sigpipe[1]) < 0) { char ebuf[1024]; VIR_ERROR(_("Failed to create pipe: %s"), virStrerror(errno, ebuf, sizeof ebuf)); diff -r d5dc15adcbea qemud/qemud.h --- a/qemud/qemud.h Fri May 08 11:58:19 2009 +0100 +++ b/qemud/qemud.h Fri May 08 16:09:05 2009 +0100 @@ -198,9 +198,6 @@ void qemudLog(int priority, const char * ATTRIBUTE_FORMAT(printf,2,3); -int qemudSetCloseExec(int fd); -int qemudSetNonBlock(int fd); - int remoteDispatchClientRequest (struct qemud_server *server, struct qemud_client *client, diff -r d5dc15adcbea tests/Makefile.am --- a/tests/Makefile.am Fri May 08 11:58:19 2009 +0100 +++ b/tests/Makefile.am Fri May 08 16:09:05 2009 +0100 @@ -125,6 +125,11 @@ if WITH_SECDRIVER_SELINUX TESTS += seclabeltest endif +if WITH_LIBVIRTD +noinst_PROGRAMS += eventtest +TESTS += eventtest +endif + TESTS += nodedevxml2xmltest path_add = $$abs_top_builddir/src$(PATH_SEPARATOR)$$abs_top_builddir/qemud @@ -226,4 +231,10 @@ qparamtest_SOURCES = \ qparamtest.c testutils.h testutils.c qparamtest_LDADD = $(LDADDS) +if WITH_LIBVIRTD +eventtest_SOURCES = \ + eventtest.c testutils.h testutils.c ../qemud/event.c +eventtest_LDADD = -lrt $(LDADDS) +endif + CLEANFILES = *.cov *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda diff -r d5dc15adcbea tests/eventtest.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/eventtest.c Fri May 08 16:09:05 2009 +0100 @@ -0,0 +1,444 @@ +/* + * eventtest.c: Test the libvirtd event loop impl + * + * Copyright (C) 2009 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 + */ + +#include + +#include +#include +#include + +#include "testutils.h" +#include "internal.h" +#include "threads.h" +#include "logging.h" +#include "../qemud/event.h" + +#define NUM_FDS 5 +#define NUM_TIME 5 + +static struct handleInfo { + int pipeFD[2]; + int fired; + int watch; + int error; + int delete; +} handles[NUM_FDS]; + +static struct timerInfo { + int timeout; + int timer; + int fired; + int error; + int delete; +} timers[NUM_TIME]; + +enum { + EV_ERROR_NONE, + EV_ERROR_WATCH, + EV_ERROR_FD, + EV_ERROR_EVENT, + EV_ERROR_DATA, +}; + +static void +testPipeReader(int watch, int fd, int events, void *data) +{ + struct handleInfo *info = data; + char one; + + info->fired = 1; + + if (watch != info->watch) { + info->error = EV_ERROR_WATCH; + return; + } + + if (fd != info->pipeFD[0]) { + info->error = EV_ERROR_FD; + return; + } + + if (!(events & VIR_EVENT_HANDLE_READABLE)) { + info->error = EV_ERROR_EVENT; + return; + } + if (read(fd, &one, 1) != 1) { + info->error = EV_ERROR_DATA; + return; + } + info->error = EV_ERROR_NONE; + + if (info->delete != -1) + virEventRemoveHandleImpl(info->delete); +} + + +static void +testTimer(int timer, void *data) +{ + struct timerInfo *info = data; + + info->fired = 1; + + if (timer != info->timer) { + info->error = EV_ERROR_WATCH; + return; + } + + info->error = EV_ERROR_NONE; + + if (info->delete != -1) + virEventRemoveTimeoutImpl(info->delete); +} + +static pthread_mutex_t eventThreadMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t eventThreadRunCond = PTHREAD_COND_INITIALIZER; +static int eventThreadRunOnce = 0; +static pthread_cond_t eventThreadJobCond = PTHREAD_COND_INITIALIZER; +static int eventThreadJobDone = 0; + + +static void *eventThreadLoop(void *data ATTRIBUTE_UNUSED) { + while (1) { + pthread_mutex_lock(&eventThreadMutex); + while (!eventThreadRunOnce) { + pthread_cond_wait(&eventThreadRunCond, &eventThreadMutex); + } + eventThreadRunOnce = 0; + pthread_mutex_unlock(&eventThreadMutex); + + virEventRunOnce(); + + pthread_mutex_lock(&eventThreadMutex); + eventThreadJobDone = 1; + pthread_cond_signal(&eventThreadJobCond); + pthread_mutex_unlock(&eventThreadMutex); + } +} + + +static int +verifyFired(int handle, int timer) +{ + int handleFired = 0; + int timerFired = 0; + int i; + for (i = 0 ; i < NUM_FDS ; i++) { + if (handles[i].fired) { + if (i != handle) { + fprintf(stderr, "FAIL Handle %d fired, but expected %d\n", i, handle); + return EXIT_FAILURE; + } else { + if (handles[i].error != EV_ERROR_NONE) { + fprintf(stderr, "FAIL Handle %d fired, but had error %d\n", i, + handles[i].error); + return EXIT_FAILURE; + } + handleFired = 1; + } + } else { + if (i == handle) { + fprintf(stderr, "FAIL Handle %d should have fired, but didn't\n", handle); + return EXIT_FAILURE; + } + } + } + if (handleFired != 1 && handle != -1) { + fprintf(stderr, "FAIL Something wierd happened, expecting handle %d\n", handle); + return EXIT_FAILURE; + } + + + for (i = 0 ; i < NUM_TIME ; i++) { + if (timers[i].fired) { + if (i != timer) { + fprintf(stderr, "FAIL Timer %d fired, but expected %d\n", i, timer); + return EXIT_FAILURE; + } else { + if (timers[i].error != EV_ERROR_NONE) { + fprintf(stderr, "FAIL Timer %d fired, but had error %d\n", i, + timers[i].error); + return EXIT_FAILURE; + } + timerFired = 1; + } + } else { + if (i == timer) { + fprintf(stderr, "FAIL Timer %d should have fired, but didn't\n", timer); + return EXIT_FAILURE; + } + } + } + if (timerFired != 1 && timer != -1) { + fprintf(stderr, "FAIL Something wierd happened, expecting timer %d\n", timer); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +static void +startJob(const char *msg, int *test) +{ + fprintf(stderr, "%2d: %s ", (*test)++, msg); + eventThreadRunOnce = 1; + eventThreadJobDone = 0; + pthread_cond_signal(&eventThreadRunCond); + pthread_mutex_unlock(&eventThreadMutex); + sched_yield(); + pthread_mutex_lock(&eventThreadMutex); +} + +static int +finishJob(int handle, int timer) +{ + struct timespec waitTime; + int rc; + clock_gettime(CLOCK_REALTIME, &waitTime); + waitTime.tv_sec += 5; + rc = 0; + while (!eventThreadJobDone && rc == 0) + rc = pthread_cond_timedwait(&eventThreadJobCond, &eventThreadMutex, &waitTime); + if (rc != 0) { + fprintf(stderr, "FAIL Timed out waiting for pipe event\n"); + return EXIT_FAILURE; + } + + if (verifyFired(handle, timer) != EXIT_SUCCESS) + return EXIT_FAILURE; + + fprintf(stderr, "OK\n"); + return EXIT_SUCCESS; +} + +static void +resetAll(void) +{ + int i; + for (i = 0 ; i < NUM_FDS ; i++) { + handles[i].fired = 0; + handles[i].error = EV_ERROR_NONE; + } + for (i = 0 ; i < NUM_TIME ; i++) { + timers[i].fired = 0; + timers[i].error = EV_ERROR_NONE; + } +} + +static int +mymain(int argc, char **argv) +{ + int ret = 0; + char *progname; + int i; + pthread_t eventThread; + char one = '1'; + int test = 1; + + progname = argv[0]; + + if (argc > 1) { + fprintf(stderr, "Usage: %s\n", progname); + return EXIT_FAILURE; + } + + for (i = 0 ; i < NUM_FDS ; i++) { + if (pipe(handles[i].pipeFD) < 0) { + fprintf(stderr, "Cannot create pipe: %d", errno); + return EXIT_FAILURE; + } + } + + if (virThreadInitialize() < 0) + return EXIT_FAILURE; + char *debugEnv = getenv("LIBVIRT_DEBUG"); + if (debugEnv && *debugEnv && *debugEnv != '0') { + if (STREQ(debugEnv, "2") || STREQ(debugEnv, "info")) + virLogSetDefaultPriority(VIR_LOG_INFO); + else if (STREQ(debugEnv, "3") || STREQ(debugEnv, "warning")) + virLogSetDefaultPriority(VIR_LOG_WARN); + else if (STREQ(debugEnv, "4") || STREQ(debugEnv, "error")) + virLogSetDefaultPriority(VIR_LOG_ERROR); + else + virLogSetDefaultPriority(VIR_LOG_DEBUG); + } + + virEventInit(); + + for (i = 0 ; i < NUM_FDS ; i++) { + handles[i].delete = -1; + handles[i].watch = + virEventAddHandleImpl(handles[i].pipeFD[0], + VIR_EVENT_HANDLE_READABLE, + testPipeReader, + &handles[i], NULL); + } + + for (i = 0 ; i < NUM_TIME ; i++) { + timers[i].delete = -1; + timers[i].timeout = -1; + timers[i].timer = + virEventAddTimeoutImpl(timers[i].timeout, + testTimer, + &timers[i], NULL); + } + + pthread_create(&eventThread, NULL, eventThreadLoop, NULL); + + pthread_mutex_lock(&eventThreadMutex); + + /* First time, is easy - just try triggering one of our + * registered handles */ + startJob("Simple write", &test); + ret = write(handles[1].pipeFD[1], &one, 1); + if (finishJob(1, -1) != EXIT_SUCCESS) + return EXIT_FAILURE; + + resetAll(); + + /* Now lets delete one before starting poll(), and + * try triggering another handle */ + virEventRemoveHandleImpl(handles[0].watch); + startJob("Deleted before poll", &test); + ret = write(handles[1].pipeFD[1], &one, 1); + if (finishJob(1, -1) != EXIT_SUCCESS) + return EXIT_FAILURE; + + resetAll(); + + /* Next lets delete *during* poll, which should interrupt + * the loop with no event showing */ + + /* NB: this case is subject to a bit of a race condition. + * We yield & sleep, and pray that the other thread gets + * scheduled before we run EventRemoveHandleImpl */ + startJob("Interrupted during poll", &test); + pthread_mutex_unlock(&eventThreadMutex); + sched_yield(); + usleep(100 * 1000); + pthread_mutex_lock(&eventThreadMutex); + virEventRemoveHandleImpl(handles[1].watch); + if (finishJob(-1, -1) != EXIT_SUCCESS) + return EXIT_FAILURE; + + resetAll(); + + /* Getting more fun, lets delete a later handle during dispatch */ + + /* NB: this case is subject to a bit of a race condition. + * Only 1 time in 3 does the 2nd write get triggered by + * before poll() exits for the first write(). We don't + * see a hard failure in other cases, so nothing to worry + * about */ + startJob("Deleted during dispatch", &test); + handles[2].delete = handles[3].watch; + ret = write(handles[2].pipeFD[1], &one, 1); + ret = write(handles[3].pipeFD[1], &one, 1); + if (finishJob(2, -1) != EXIT_SUCCESS) + return EXIT_FAILURE; + + resetAll(); + + /* Extreme fun, lets delete ourselves during dispatch */ + startJob("Deleted during dispatch", &test); + handles[2].delete = handles[2].watch; + ret = write(handles[2].pipeFD[1], &one, 1); + if (finishJob(2, -1) != EXIT_SUCCESS) + return EXIT_FAILURE; + + resetAll(); + + + + /* Run a timer on its own */ + virEventUpdateTimeoutImpl(timers[1].timer, 100); + startJob("Firing a timer", &test); + if (finishJob(-1, 1) != EXIT_SUCCESS) + return EXIT_FAILURE; + virEventUpdateTimeoutImpl(timers[1].timer, -1); + + resetAll(); + + /* Now lets delete one before starting poll(), and + * try triggering another timer */ + virEventUpdateTimeoutImpl(timers[1].timer, 100); + virEventRemoveTimeoutImpl(timers[0].timer); + startJob("Deleted before poll", &test); + if (finishJob(-1, 1) != EXIT_SUCCESS) + return EXIT_FAILURE; + virEventUpdateTimeoutImpl(timers[1].timer, -1); + + resetAll(); + + /* Next lets delete *during* poll, which should interrupt + * the loop with no event showing */ + + /* NB: this case is subject to a bit of a race condition. + * We yield & sleep, and pray that the other thread gets + * scheduled before we run EventRemoveTimeoutImpl */ + startJob("Interrupted during poll", &test); + pthread_mutex_unlock(&eventThreadMutex); + sched_yield(); + usleep(100 * 1000); + pthread_mutex_lock(&eventThreadMutex); + virEventRemoveTimeoutImpl(timers[1].timer); + if (finishJob(-1, -1) != EXIT_SUCCESS) + return EXIT_FAILURE; + + resetAll(); + + /* Getting more fun, lets delete a later timer during dispatch */ + + /* NB: this case is subject to a bit of a race condition. + * Only 1 time in 3 does the 2nd write get triggered by + * before poll() exits for the first write(). We don't + * see a hard failure in other cases, so nothing to worry + * about */ + virEventUpdateTimeoutImpl(timers[2].timer, 100); + virEventUpdateTimeoutImpl(timers[3].timer, 100); + startJob("Deleted during dispatch", &test); + timers[2].delete = timers[3].timer; + if (finishJob(-1, 2) != EXIT_SUCCESS) + return EXIT_FAILURE; + virEventUpdateTimeoutImpl(timers[2].timer, -1); + + resetAll(); + + /* Extreme fun, lets delete ourselves during dispatch */ + virEventUpdateTimeoutImpl(timers[2].timer, 100); + startJob("Deleted during dispatch", &test); + timers[2].delete = timers[2].timer; + if (finishJob(-1, 2) != EXIT_SUCCESS) + return EXIT_FAILURE; + + for (i = 0 ; i < NUM_FDS ; i++) + virEventRemoveHandleImpl(handles[i].watch); + for (i = 0 ; i < NUM_TIME ; i++) + virEventRemoveTimeoutImpl(timers[i].timer); + + + //pthread_kill(eventThread, SIGTERM); + + return EXIT_SUCCESS; +} + + +VIRT_TEST_MAIN(mymain) diff -r d5dc15adcbea tests/testutils.h --- a/tests/testutils.h Fri May 08 11:58:19 2009 +0100 +++ b/tests/testutils.h Fri May 08 16:09:05 2009 +0100 @@ -11,6 +11,8 @@ #ifndef __VIT_TEST_UTILS_H__ #define __VIT_TEST_UTILS_H__ +#include + double virtTestCountAverage(double *items, int nitems);