--- /dev/null
+*.o
+*.d
+xl_event_test
--- /dev/null
+SRCS = thread_test.c xl_eventloop.c testcase.c xl_event_test.c
+CFLAGS = -Wall -Wextra -Werror -pedantic -g
+LDFLAGS = -pthread -lxenlight
+
+all: xl_event_test
+xl_event_test: $(SRCS:.c=.o)
+
+%.d: %.c
+ $(CC) -M $< > $@
+
+.PHONY: clean
+clean:
+ rm -f $(SRCS:.c=.o) $(SRCS:.c=.d) xl_event_test
+
+-include $(SRCS:.c=.d)
--- /dev/null
+#include <libxl.h>
+#include "thread_test.h"
+#include "xl_eventloop.h"
+
+void *
+testcase(struct test *tc)
+{
+ uint32_t domid;
+ libxl_domain_config dc;
+
+ printf("thread started\n");
+
+ init_domain_config(&dc, "badger", "/root/vmlinuz-4.0.4-301.fc22.x86_64");
+ do_domain_create(tc, &dc, &domid);
+ wait_for(tc, EV_LIBXL_CALLBACK); /* need to be able to distingish out callback from others */
+ libxl_domain_config_dispose(&dc);
+ printf("domain %d created\n", domid);
+
+ libxl_domain_unpause(tc->ctx, domid);
+ printf("domain %d unpaused\n", domid);
+
+ wait_for_n(tc, EV_EVENTLOOP, 10);
+ do_domain_suspend(tc, domid);
+ wait_for(tc, EV_LIBXL_CALLBACK);
+ printf("domain %d suspended\n", domid);
+
+ pthread_exit(NULL);
+}
+
--- /dev/null
+#ifndef __TESTCASE_H
+#define __TESTCASE_H
+
+void *testcase(struct test *tc);
+
+#endif /* __TESTCASE_H */
--- /dev/null
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+
+#include "thread_test.h"
+#include "xl_eventloop.h"
+
+
+int
+test_spawn(struct test *tc, xentoollog_logger *logger,
+ void *(*fn)(struct test *))
+{
+ /* Initialize the test structure */
+ libxl_ctx_alloc(&tc->ctx, LIBXL_VERSION, 0, logger);
+ register_callbacks(tc);
+
+ /* Initialize the mailbox */
+ pthread_mutex_init(&tc->mailbox_lock, NULL);
+ pthread_cond_init(&tc->producer_cv, NULL);
+ pthread_cond_init(&tc->consumer_cv, NULL);
+ tc->mailbox.type = EV_NONE;
+
+ /* Spawn a thread to run the test case */
+ return pthread_create(&tc->thread, NULL,
+ (void *(*)(void *)) fn, tc);
+}
+
+
+void
+test_destroy(struct test *tc) {
+ pthread_mutex_destroy(&tc->mailbox_lock);
+ pthread_cond_destroy(&tc->producer_cv);
+ pthread_cond_destroy(&tc->consumer_cv);
+ libxl_ctx_free(tc->ctx);
+}
+
+
+void
+send_event(struct test *tc, struct event ev)
+{
+ pthread_mutex_lock(&tc->mailbox_lock);
+ while (tc->mailbox.type != EV_NONE) {
+ pthread_cond_wait(&tc->producer_cv, &tc->mailbox_lock);
+ }
+ tc->mailbox = ev;
+ pthread_cond_signal(&tc->consumer_cv);
+ pthread_mutex_unlock(&tc->mailbox_lock);
+}
+
+
+void
+recv_event(struct test *tc, struct event *ev)
+{
+ pthread_mutex_lock(&tc->mailbox_lock);
+ while (tc->mailbox.type == EV_NONE) {
+ pthread_cond_wait(&tc->consumer_cv, &tc->mailbox_lock);
+ }
+ *ev = tc->mailbox;
+ tc->mailbox.type = EV_NONE;
+ pthread_cond_signal(&tc->producer_cv);
+ pthread_mutex_unlock(&tc->mailbox_lock);
+}
+
+
+/* Wait until the an event matching the expected mask is posted.
+ Ignores and discards any non-matching events received in the meantime. */
+void
+wait_for(struct test *tc, enum event_type expected)
+{
+ struct event received;
+ do {
+ recv_event(tc, &received);
+ } while(!(received.type & expected));
+}
+
+
+/* Wait until the the specified number of the expected events are posted.
+ Ignores and discards any other events received in the meantime. */
+void
+wait_for_n(struct test *tc, enum event_type expected, int count)
+{
+ while(count--) {
+ wait_for(tc, expected);
+ }
+}
+
+
+void
+send_fd_event(struct test *tc, int fd)
+{
+ struct event ev;
+ ev.type = EV_FD_EVENT;
+ ev.u.fd = fd;
+ send_event(tc, ev);
+ printf("-> EV_FD_EVENT\n");
+}
+
+
+void
+send_libxl_callback_event(struct test *tc)
+{
+ struct event ev;
+ ev.type = EV_LIBXL_CALLBACK;
+ send_event(tc, ev);
+ printf("-> EV_LIBXL_CALLBACK\n");
+}
+
+
+void
+send_eventloop(struct test *tc)
+{
+ struct event ev;
+ ev.type = EV_EVENTLOOP;
+ send_event(tc, ev);
+ printf("-> EV_EVENTLOOP_TICK\n");
+}
+
--- /dev/null
+#ifndef __THREAD_TEST_H
+#define __THREAD_TEST_H
+
+#include <libxl.h>
+#include <pthread.h>
+
+enum event_type {
+ EV_NONE = 0x0000,
+ EV_TEST_START = 0x0001,
+ EV_LIBXL_CALLBACK = 0x0002,
+ EV_FD_EVENT = 0x0004,
+ EV_EVENTLOOP = 0x0008,
+ EV_ANY = 0xffff
+};
+
+
+struct event {
+ enum event_type type;
+ union {
+ int fd;
+ } u;
+};
+
+struct test {
+ /*
+ * Test task structure.
+ */
+
+ /*
+ * Pointer to the libxl context structure.
+ * The memory which this points to is allocated and
+ * owned by libxl.
+ */
+ libxl_ctx *ctx;
+
+ /*
+ * Structures containing the hooks which libxl
+ * uses to register its timers and its interest in
+ * file descriptors.
+ * These structures must outlive the libxl context
+ * in which they are used, so it makes sense to
+ * keep them here alongside the ctx pointer.
+ */
+ libxl_event_hooks xl_ev_hooks;
+ libxl_osevent_hooks xl_os_ev_hooks;
+
+ /*
+ * Structure defining per-function callbacks.
+ * Must outlive the asynchronous call which uses it.
+ */
+ libxl_asyncop_how ao_how;
+
+ /* The thread running the test */
+ pthread_t thread;
+
+ /* Test thread's mailbox and locks */
+ pthread_mutex_t mailbox_lock;
+ pthread_cond_t producer_cv;
+ pthread_cond_t consumer_cv;
+ struct event mailbox;
+};
+
+
+int test_spawn(struct test *tc, xentoollog_logger *logger,
+ void *(*fn)(struct test *));
+void test_destroy(struct test *tc);
+void send_event(struct test *tc, struct event ev);
+void recv_event(struct test *tc, struct event *ev);
+void wait_for(struct test *tc, enum event_type expected);
+void wait_for_n(struct test *tc, enum event_type expected, int count);
+void send_fd_event(struct test *tc, int fd);
+void send_libxl_callback_event(struct test *tc);
+void send_eventloop(struct test *tc);
+
+#endif /* __THREAD_TEST_H */
--- /dev/null
+int
+main(int argc __attribute__((unused)),
+ char **argv __attribute__((unused)))
+{
+ struct test t;
+ test_init(&t);
+ test_spawn(&t, testcase);
+
+ send_fd_event(&t, 0);
+ send_fd_event(&t, 1);
+ send_fd_event(&t, 2);
+ send_fd_event(&t, 3);
+ send_fd_event(&t, 4);
+
+ send_libxl_callback_event(&t);
+ send_libxl_callback_event(&t);
+
+ send_eventloop(&t);
+
+ pthread_join(t.thread, NULL);
+ test_destroy(&t);
+ pthread_exit(NULL);
+}
+
--- /dev/null
+#include <libxl_event.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <unistd.h>
+
+#include <sys/select.h>
+
+#include "xl_eventloop.h"
+#include "thread_test.h"
+#include "testcase.h"
+
+int
+main(int argc __attribute__((unused)), char **argv __attribute__((unused)))
+{
+ struct test t;
+ xentoollog_logger *logger;
+
+ init_pollfds(pollfds, NUM_POLL_FDS);
+
+ logger = (xentoollog_logger*)
+ xtl_createlogger_stdiostream(stderr, XTL_PROGRESS, 0);
+
+ if (!!test_spawn(&t, logger, testcase)) {
+ perror("Failed to spawn test thread");
+ exit(EXIT_FAILURE);
+ }
+
+ while (1) {
+ int i, rc;
+
+ rc = poll(pollfds, NUM_POLL_FDS, 2000);
+ if (rc == 0) {
+ send_eventloop(&t);
+ continue;
+ }
+
+ for (i = 0; i < NUM_POLL_FDS; i++) {
+
+ if (pollfds[i].revents & POLLIN) {
+ fprintf(stderr, "fd %d readable\n", pollfds[i].fd);
+ }
+ if (pollfds[i].revents & POLLOUT) {
+ fprintf(stderr, "fd %d writeable\n", pollfds[i].fd);
+ }
+ if (pollfds[i].revents & POLLPRI) {
+ fprintf(stderr, "fd %d priority readable\n", pollfds[i].fd);
+ }
+ if (pollfds[i].revents & POLLERR) {
+ fprintf(stderr, "fd %d output error\n", pollfds[i].fd);
+ }
+ if (pollfds[i].revents & POLLHUP) {
+ fprintf(stderr, "fd %d hung up\n", pollfds[i].fd);
+ }
+ if (pollfds[i].revents & POLLNVAL) {
+ fprintf(stderr, "fd %d not open\n", pollfds[i].fd);
+ }
+
+ if (pollfds[i].revents != 0) {
+ struct libxl_task *lxt = &libxl_tasks[i];
+ struct pollfd *pfd = &pollfds[i];
+
+ libxl_osevent_occurred_fd(lxt->task->ctx, lxt->for_libxl,
+ pfd->fd, pfd->events, pfd->revents);
+ send_fd_event(&t, pfd->fd);
+ }
+ }
+ }
+
+ test_destroy(&t);
+ xtl_logger_destroy(logger);
+ exit(EXIT_SUCCESS);
+}
--- /dev/null
+#include <assert.h>
+#include <libxl.h>
+#include <libxl_event.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <unistd.h>
+
+#include <sys/select.h>
+#include <string.h>
+#include "xl_eventloop.h"
+#include "thread_test.h"
+
+/*
+ * These operations may need to be protected by a lock, since
+ * the worker thread will make calls which require file descriptors
+ * to be registered and the event loop will deregister them.
+ * Alternatively, the test case will need to ask for file descriptors
+ * to be registered and deregistered by sending messages to the main
+ * event loop.
+ */
+
+void
+init_pollfds(struct pollfd *pollfds, int numfds)
+{
+ int i;
+ for (i = 0; i < numfds; i++) {
+ pollfds[i].fd = -1;
+ pollfds[i].events = 0;
+ pollfds[i].revents = 0;
+ }
+}
+
+int
+add_poll_fd(struct pollfd *pollfds, int numfds, int fd, short events)
+{
+ int i;
+ for (i = 0; i < numfds; i++) {
+ if (pollfds[i].fd == -1) {
+ pollfds[i].fd = fd;
+ pollfds[i].events = events;
+ pollfds[i].revents = 0;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+int
+modify_poll_fd(struct pollfd *pollfds, int numfds, int slot, int fd, short events)
+{
+ assert(slot < numfds);
+ assert(pollfds[slot].fd == fd);
+ pollfds[slot].events = events;
+ return 0;
+}
+
+int
+remove_poll_fd(struct pollfd *pollfds, int numfds, int slot, int fd)
+{
+ assert(slot < numfds);
+ assert (pollfds[slot].fd != -1);
+ assert (pollfds[slot].fd == fd);
+ pollfds[slot].fd = -1;
+ pollfds[slot].events = 0;
+ pollfds[slot].revents = 0;
+ return 0;
+}
+
+
+int
+fd_register(void *user, int fd, void **for_app_registration_out,
+ short events, void *for_libxl)
+{
+ int slot;
+ struct test *t;
+ struct libxl_task *lxt;
+ t = user;
+
+ slot = add_poll_fd(pollfds, NUM_POLL_FDS, fd, events);
+ lxt = &libxl_tasks[slot];
+ lxt->task = t;
+ lxt->slot = slot;
+ lxt->for_libxl = for_libxl;
+ *for_app_registration_out = lxt;
+
+ return 0;
+}
+
+int
+fd_modify(void *user, int fd, void **for_app_registration_update,
+ short events)
+{
+ struct test *t;
+ struct libxl_task *lxt;
+
+ assert(user);
+ assert(for_app_registration_update);
+ assert(*for_app_registration_update);
+
+ t = user;
+ lxt = *for_app_registration_update;
+
+ modify_poll_fd(pollfds, NUM_POLL_FDS, lxt->slot, fd, events);
+
+ return 0;
+}
+
+void
+fd_deregister(void *user, int fd, void *for_app_registration)
+{
+ struct test *t;
+ struct libxl_task *lxt;
+
+
+ t = user;
+ lxt = for_app_registration;
+
+ remove_poll_fd(pollfds, NUM_POLL_FDS, lxt->slot, fd);
+
+}
+
+int
+timeout_register(void *user __attribute__((unused)),
+ void **for_app_registration_out __attribute__((unused)),
+ struct timeval abs __attribute__((unused)),
+ void *for_libxl __attribute__((unused)))
+{
+ return 0;
+}
+
+/* only ever called with abs={0,0}, meaning ASAP */
+int
+timeout_modify(void *user __attribute__((unused)), void **for_app_registration_update __attribute__((unused)),
+ struct timeval abs __attribute__((unused)))
+{
+ return 0;
+}
+
+/* will never be called */
+void
+timeout_deregister(void *user __attribute__((unused)), void *for_app_registration __attribute__((unused)))
+{
+}
+
+
+void
+print_domain_config(libxl_ctx *ctx, char *msg, libxl_domain_config *dc) {
+ char *json = libxl_domain_config_to_json(ctx, dc);
+ printf("%s: %s\n", msg, json);
+ free(json);
+}
+
+
+void
+libxlEventHandler(void *data __attribute__((unused)), /* const */ libxl_event *event __attribute__((unused)))
+{
+}
+
+void
+register_callbacks(struct test *t)
+{
+ /*
+ * Register the hook functions which libxl will call
+ * to register its timers and its interest in file
+ * descriptors used for operations such as suspend
+ * and resume.
+ * The structs containing these hooks must outlive
+ * the xl context.
+ * Each callback will be called with a pointer to the
+ * task structure.
+ */
+
+ /* Register ordinary async callbacks */
+ t->xl_ev_hooks.event_occurs_mask = LIBXL_EVENTMASK_ALL;
+ t->xl_ev_hooks.event_occurs = libxlEventHandler;
+ t->xl_ev_hooks.disaster = 0;
+ libxl_event_register_callbacks(t->ctx, &t->xl_ev_hooks, t);
+
+ /* Register eventloop integration callbacks */
+ t->xl_os_ev_hooks.fd_register = fd_register;
+ t->xl_os_ev_hooks.fd_modify = fd_modify;
+ t->xl_os_ev_hooks.fd_deregister = fd_deregister;
+ t->xl_os_ev_hooks.timeout_register = timeout_register;
+ t->xl_os_ev_hooks.timeout_modify = timeout_modify;
+ t->xl_os_ev_hooks.timeout_deregister = timeout_deregister;
+ libxl_osevent_register_hooks(t->ctx, &t->xl_os_ev_hooks, t);
+}
+
+void domain_create_cb(libxl_ctx *ctx, int rc, void *for_callback);
+
+void
+init_domain_config(libxl_domain_config *dc, char *name, char *kernel)
+{
+ libxl_device_disk *disk;
+
+ libxl_domain_config_init(dc);
+
+ /* should we be using xlu_cfg_replace_string? */
+ dc->c_info.name = strdup(name);
+ dc->c_info.type = LIBXL_DOMAIN_TYPE_PV;
+ dc->b_info.type = LIBXL_DOMAIN_TYPE_PV;
+ dc->b_info.max_memkb = 512*1024;
+ dc->b_info.u.pv.kernel = strdup(kernel);
+ dc->b_info.u.pv.ramdisk = strdup("/root/foobar.img");
+ dc->b_info.cmdline = strdup("root=/dev/xvda1 selinux=0 console=hvc0");
+
+ /* need to add devices here; create returns immediatly otherwise
+ use xlu_disk_parse? xlu_cfg_readfile? */
+ dc->num_disks = 0;
+ dc->disks = NULL;
+
+ dc->disks = malloc(sizeof(*dc->disks) * 2);
+ dc->num_disks = 2;
+
+ disk = &dc->disks[0];
+ libxl_device_disk_init(disk);
+ disk->pdev_path = strdup("/root/Fedora-Cloud-Base-22-20150521.x86_64.qcow2");
+ disk->vdev = strdup("xvda");
+ disk->format = LIBXL_DISK_FORMAT_QCOW2;
+ disk->readwrite = 1;
+
+ disk = &dc->disks[1];
+ libxl_device_disk_init(disk);
+ disk->pdev_path = strdup("/root/init.iso");
+ disk->vdev = strdup("xvdb");
+ disk->format = LIBXL_DISK_FORMAT_RAW;
+ disk->removable = 1;
+ disk->is_cdrom = 1;
+}
+
+int
+do_domain_create(struct test *t, libxl_domain_config *dc, uint32_t *domid_out)
+{
+ /* speak to andy cooper about valgrind runes to handle xen hypercalls */
+
+ t->ao_how.callback = domain_create_cb;
+ t->ao_how.u.for_callback = t; /* need to carry the other data to be freed */
+
+ return libxl_domain_create_new(t->ctx, dc, domid_out, &t->ao_how, 0);
+}
+
+void
+domain_create_cb(libxl_ctx *ctx __attribute__((unused)),
+ int rc __attribute__((unused)), void *for_callback)
+{
+ struct test *tc = for_callback;
+ send_libxl_callback_event(tc);
+}
+
+
+void domain_suspend_cb(libxl_ctx *ctx, int rc, void *for_callback);
+
+int
+do_domain_suspend(struct test *t, uint32_t domid)
+{
+ /* need to issue a suspend in order to get an event channel wait
+ * which should ask to register an fd or something else for us above */
+ int fd = open("/tmp/suspend", O_RDWR|O_CREAT|O_TRUNC);
+
+ t->ao_how.callback = domain_suspend_cb;
+ /* t->ao_how.u.for_callback = (void*) fd; could rely on the test case to provide and close the fd - it can track it in a local variable */
+
+ return libxl_domain_suspend(t->ctx, domid, fd, LIBXL_SUSPEND_LIVE, &t->ao_how);
+}
+
+void
+domain_suspend_cb(libxl_ctx *ctx __attribute__((unused)),
+ int rc __attribute__((unused)),
+ void *for_callback __attribute__((unused)))
+{
+ /* struct test *t = for_callback; */
+ /* close(t->in_args.domain_suspend.fd); */
+ printf("< domain_suspend_cb()\n");
+}
+
--- /dev/null
+#ifndef __XL_EVENTLOOP_H
+#define __XL_EVENTLOOP_H
+
+#include <libxl.h>
+#include <libxl_event.h>
+
+struct test;
+struct libxl_task {
+ struct test *task;
+ int slot;
+ void *for_libxl;
+};
+
+
+enum { NUM_POLL_FDS = 10 };
+struct pollfd pollfds[NUM_POLL_FDS];
+struct libxl_task libxl_tasks[NUM_POLL_FDS];
+
+void init_pollfds(struct pollfd *pollfds, int numfds);
+
+void init_domain_config(libxl_domain_config *dc, char *name, char *kernel);
+
+void register_callbacks(struct test *t);
+int do_domain_create(struct test *t, libxl_domain_config *dc, uint32_t *domid_out);
+int do_domain_suspend(struct test *t, uint32_t domid);
+
+#endif /* __XL_EVENTLOOP_H */
--- /dev/null
+/*
+gcc -Wall -Werror -Wextra -pedantic -g -lxenlight -lxlutil xlu_test.c -o xlu_test
+*/
+
+#include <assert.h>
+#include <libxl.h>
+#include <libxlutil.h>
+#include <libxl_event.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <unistd.h>
+
+#include <sys/select.h>
+#include <string.h>
+
+void
+disk_test(XLU_Config *config, const char *spec)
+{
+ libxl_device_disk disk;
+ libxl_device_disk_init(&disk);
+ xlu_disk_parse(config, 1, &spec, &disk);
+}
+
+int
+main(int argc, char **argv)
+{
+ int err;
+ XLU_Config *config;
+ XLU_ConfigList *config_list;
+ int count;
+
+ FILE *report;
+ char *report_filename = "/tmp/xlu.out";
+ const char *response;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s: no config file provided\n", argv[0]);
+ exit(-1);
+ }
+
+ report = fopen(report_filename, "w+");
+
+ config = xlu_cfg_init(report, report_filename);
+ if (!config) {
+ perror("xlu_cfg_init");
+ exit(-1);
+ }
+
+ err = xlu_cfg_readfile(config, argv[1]);
+ if (err != 0) {
+ perror("xlu_cfg_readfile");
+ exit(-1);
+ }
+
+ err = xlu_cfg_get_string(config, "name", &response, 1);
+ if (err != 0) {
+ perror("xlu_cfg_get_string");
+ exit(-1);
+ }
+ printf("name: %s\n", response);
+
+ err = xlu_cfg_get_string(config, "kernel", &response, 1);
+ if (err != 0) {
+ perror("xlu_cfg_get_string");
+ exit(-1);
+ }
+ printf("kernel: %s\n", response);
+
+ err = xlu_cfg_get_list(config, "disk", &config_list, &count, 1);
+ if (err != 0) {
+ perror("xlu_cfg_get_string");
+ exit(-1);
+ }
+ printf("disk: %d items\n", count);
+
+ disk_test(config, "qcow2:/root/Fedora-Cloud-Base-22-20150521.x86_64.qcow2,xvda,w");
+ disk_test(config, "file:/root/init.iso,xvdb:cdrom,r");
+ xlu_cfg_destroy(config);
+ exit(0);
+}
+