diff --git a/Makefile b/Makefile
index 35c7a2a..ea127f9 100644
--- a/Makefile
+++ b/Makefile
@@ -153,7 +153,7 @@ endif
 qemu-img.o: qemu-img-cmds.h
 qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS)
 
-tools-obj-y = $(oslib-obj-y) $(trace-obj-y) qemu-tool.o qemu-timer.o \
+tools-obj-y = $(oslib-obj-y) $(event-obj-y) $(trace-obj-y) qemu-tool.o qemu-timer.o \
 	qemu-timer-common.o main-loop.o notify.o iohandler.o cutils.o async.o
 tools-obj-$(CONFIG_POSIX) += compatfd.o
 
diff --git a/Makefile.objs b/Makefile.objs
index f308b57..377bfe2 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -24,6 +24,9 @@ oslib-obj-y = osdep.o
 oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o
 oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o
 
+event-obj-$(CONFIG_POSIX) = qemu-event-posix.o
+event-obj-$(CONFIG_WIN32) = qemu-event-win32.o
+
 #######################################################################
 # coroutines
 coroutine-obj-y = qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
@@ -97,7 +100,7 @@ common-obj-y += $(net-obj-y)
 common-obj-y += $(qom-obj-twice-y)
 common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX))
 common-obj-y += readline.o console.o cursor.o
-common-obj-y += $(oslib-obj-y)
+common-obj-y += $(oslib-obj-y) $(event-obj-y)
 common-obj-$(CONFIG_WIN32) += os-win32.o
 common-obj-$(CONFIG_POSIX) += os-posix.o
 
diff --git a/qemu-event-posix.c b/qemu-event-posix.c
new file mode 100644
index 0000000..6138168
--- /dev/null
+++ b/qemu-event-posix.c
@@ -0,0 +1,110 @@
+/*
+ * Posix implementations of event signaling service
+ *
+ * Copyright Red Hat, Inc. 2012
+ * Copyright Siemens AG 2012
+ *
+ * Author:
+ *  Paolo Bonzini <pbonzini@redhat.com>
+ *  Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#include "qemu-thread.h"
+#include "qemu-common.h"
+#include "main-loop.h"
+
+#ifdef CONFIG_EVENTFD
+#include <sys/eventfd.h>
+#endif
+
+void qemu_event_init(QemuEvent *event, bool signaled)
+{
+    int fds[2];
+    int ret;
+
+#ifdef CONFIG_EVENTFD
+    ret = eventfd(signaled, EFD_NONBLOCK | EFD_CLOEXEC);
+    if (ret >= 0) {
+        event->rfd = ret;
+        event->wfd = dup(ret);
+        if (event->wfd < 0) {
+            qemu_error_exit(errno, __func__);
+        }
+        qemu_set_cloexec(event->wfd);
+        return;
+    }
+    if (errno != ENOSYS) {
+        qemu_error_exit(errno, __func__);
+    }
+    /* fall back to pipe-based support */
+#endif
+
+    ret = qemu_pipe(fds);
+    if (ret < 0) {
+        qemu_error_exit(errno, __func__);
+    }
+    event->rfd = fds[0];
+    event->wfd = fds[1];
+    if (signaled) {
+        qemu_event_signal(event);
+    }
+}
+
+void qemu_event_destroy(QemuEvent *event)
+{
+    close(event->rfd);
+    close(event->wfd);
+}
+
+int qemu_event_get_signal_fd(QemuEvent *event)
+{
+    return event->wfd;
+}
+
+int qemu_event_get_poll_fd(QemuEvent *event)
+{
+    return event->rfd;
+}
+
+void qemu_event_set_handler(QemuEvent *event, QemuEventHandler *handler,
+                            void *opaque)
+{
+    qemu_set_fd_handler2(event->rfd, NULL, (IOHandler *)handler, NULL, opaque);
+}
+
+void qemu_event_signal(QemuEvent *event)
+{
+    /* Write 8 bytes to be compatible with eventfd. */
+    static const uint64_t val = 1;
+    ssize_t ret;
+
+    do {
+        ret = write(event->wfd, &val, sizeof(val));
+    } while (ret < 0 && errno == EINTR);
+
+    if (ret < 0 && errno != EAGAIN) {
+        qemu_error_exit(errno, __func__);
+    }
+}
+
+bool qemu_event_consume(QemuEvent *event)
+{
+    bool was_set = false;
+    uint64_t val;
+    int ret;
+
+    while (1) {
+        ret = read(event->rfd, &val, sizeof(val));
+        if (ret == sizeof(val)) {
+            was_set = true;
+            continue;
+        }
+        if (ret < 0 && errno == EAGAIN) {
+            return was_set;
+        }
+        qemu_error_exit(errno, __func__);
+    }
+}
diff --git a/qemu-event-win32.c b/qemu-event-win32.c
new file mode 100644
index 0000000..38fe9ae
--- /dev/null
+++ b/qemu-event-win32.c
@@ -0,0 +1,65 @@
+/*
+ * Win32 implementations of event signaling service
+ *
+ * Copyright Red Hat, Inc. 2012
+ * Copyright Siemens AG 2012
+ *
+ * Author:
+ *  Paolo Bonzini <pbonzini@redhat.com>
+ *  Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#include <stdbool.h>
+#include "qemu-thread.h"
+#include "qemu-common.h"
+#include "main-loop.h"
+
+void qemu_event_init(QemuEvent *event, bool signaled)
+{
+    event->event = CreateEvent(NULL, FALSE, signaled, NULL);
+    if (!event->event) {
+        qemu_error_exit(GetLastError(), __func__);
+    }
+}
+
+void qemu_event_destroy(QemuEvent *event)
+{
+    CloseHandle(event->event);
+}
+
+int qemu_event_get_signal_fd(QemuEvent *event)
+{
+    /* unsupported on Win32 */
+    abort();
+}
+
+void qemu_event_set_handler(QemuEvent *event, QemuEventHandler *handler,
+                            void *opaque)
+{
+    if (handler) {
+        qemu_add_wait_object(event->event, (IOHandler *)handler, opaque);
+    } else {
+        qemu_del_wait_object(event->event, (IOHandler *)handler, opaque);
+    }
+}
+
+void qemu_event_signal(QemuEvent *event)
+{
+    if (!SetEvent(event->event)) {
+        qemu_error_exit(GetLastError(), __func__);
+    }
+}
+
+bool qemu_event_consume(QemuEvent *event)
+{
+    DWORD ret;
+
+    ret = WaitForSingleObject(event->event, 0);
+    if (ret == WAIT_FAILED) {
+        qemu_error_exit(GetLastError(), __func__);
+    }
+    return ret == WAIT_OBJECT_0;
+}
diff --git a/qemu-thread-posix.h b/qemu-thread-posix.h
index 9f00524..95fbb45 100644
--- a/qemu-thread-posix.h
+++ b/qemu-thread-posix.h
@@ -11,6 +11,11 @@ struct QemuCond {
     pthread_cond_t cond;
 };
 
+struct QemuEvent {
+    int rfd;
+    int wfd;
+};
+
 struct QemuThread {
     pthread_t thread;
 };
diff --git a/qemu-thread-win32.h b/qemu-thread-win32.h
index b9d1be8..d1b7631 100644
--- a/qemu-thread-win32.h
+++ b/qemu-thread-win32.h
@@ -13,6 +13,10 @@ struct QemuCond {
     HANDLE continue_event;
 };
 
+struct QemuEvent {
+    HANDLE event;
+};
+
 typedef struct QemuThreadData QemuThreadData;
 struct QemuThread {
     QemuThreadData *data;
diff --git a/qemu-thread.h b/qemu-thread.h
index 10f2c51..a53384f 100644
--- a/qemu-thread.h
+++ b/qemu-thread.h
@@ -5,8 +5,11 @@
 
 typedef struct QemuMutex QemuMutex;
 typedef struct QemuCond QemuCond;
+typedef struct QemuEvent QemuEvent;
 typedef struct QemuThread QemuThread;
 
+typedef void QemuEventHandler(void *opaque);
+
 #ifdef _WIN32
 #include "qemu-thread-win32.h"
 #else
@@ -28,6 +31,15 @@ void qemu_mutex_unlock(QemuMutex *mutex);
 void qemu_cond_init(QemuCond *cond);
 void qemu_cond_destroy(QemuCond *cond);
 
+void qemu_event_init(QemuEvent *event, bool signaled);
+void qemu_event_destroy(QemuEvent *event);
+int qemu_event_get_signal_fd(QemuEvent *event);
+int qemu_event_get_poll_fd(QemuEvent *event);
+void qemu_event_set_handler(QemuEvent *event, QemuEventHandler *handler,
+                            void *opaque);
+void qemu_event_signal(QemuEvent *event);
+bool qemu_event_consume(QemuEvent *event);
+
 /*
  * IMPORTANT: The implementation does not guarantee that pthread_cond_signal
  * and pthread_cond_broadcast can be called except while the same mutex is
