Merge remote-tracking branch 'pmaydell/tags/pull-target-arm-20130820' into staging
[sdk/emulator/qemu.git] / aio-win32.c
1 /*
2  * QEMU aio implementation
3  *
4  * Copyright IBM Corp., 2008
5  * Copyright Red Hat Inc., 2012
6  *
7  * Authors:
8  *  Anthony Liguori   <aliguori@us.ibm.com>
9  *  Paolo Bonzini     <pbonzini@redhat.com>
10  *
11  * This work is licensed under the terms of the GNU GPL, version 2.  See
12  * the COPYING file in the top-level directory.
13  *
14  * Contributions after 2012-01-13 are licensed under the terms of the
15  * GNU GPL, version 2 or (at your option) any later version.
16  */
17
18 #include "qemu-common.h"
19 #include "block/block.h"
20 #include "qemu/queue.h"
21 #include "qemu/sockets.h"
22
23 struct AioHandler {
24     EventNotifier *e;
25     EventNotifierHandler *io_notify;
26     GPollFD pfd;
27     int deleted;
28     QLIST_ENTRY(AioHandler) node;
29 };
30
31 void aio_set_event_notifier(AioContext *ctx,
32                             EventNotifier *e,
33                             EventNotifierHandler *io_notify)
34 {
35     AioHandler *node;
36
37     QLIST_FOREACH(node, &ctx->aio_handlers, node) {
38         if (node->e == e && !node->deleted) {
39             break;
40         }
41     }
42
43     /* Are we deleting the fd handler? */
44     if (!io_notify) {
45         if (node) {
46             g_source_remove_poll(&ctx->source, &node->pfd);
47
48             /* If the lock is held, just mark the node as deleted */
49             if (ctx->walking_handlers) {
50                 node->deleted = 1;
51                 node->pfd.revents = 0;
52             } else {
53                 /* Otherwise, delete it for real.  We can't just mark it as
54                  * deleted because deleted nodes are only cleaned up after
55                  * releasing the walking_handlers lock.
56                  */
57                 QLIST_REMOVE(node, node);
58                 g_free(node);
59             }
60         }
61     } else {
62         if (node == NULL) {
63             /* Alloc and insert if it's not already there */
64             node = g_malloc0(sizeof(AioHandler));
65             node->e = e;
66             node->pfd.fd = (uintptr_t)event_notifier_get_handle(e);
67             node->pfd.events = G_IO_IN;
68             QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
69
70             g_source_add_poll(&ctx->source, &node->pfd);
71         }
72         /* Update handler with latest information */
73         node->io_notify = io_notify;
74     }
75
76     aio_notify(ctx);
77 }
78
79 bool aio_pending(AioContext *ctx)
80 {
81     AioHandler *node;
82
83     QLIST_FOREACH(node, &ctx->aio_handlers, node) {
84         if (node->pfd.revents && node->io_notify) {
85             return true;
86         }
87     }
88
89     return false;
90 }
91
92 bool aio_poll(AioContext *ctx, bool blocking)
93 {
94     AioHandler *node;
95     HANDLE events[MAXIMUM_WAIT_OBJECTS + 1];
96     bool progress;
97     int count;
98
99     progress = false;
100
101     /*
102      * If there are callbacks left that have been queued, we need to call then.
103      * Do not call select in this case, because it is possible that the caller
104      * does not need a complete flush (as is the case for qemu_aio_wait loops).
105      */
106     if (aio_bh_poll(ctx)) {
107         blocking = false;
108         progress = true;
109     }
110
111     /*
112      * Then dispatch any pending callbacks from the GSource.
113      *
114      * We have to walk very carefully in case qemu_aio_set_fd_handler is
115      * called while we're walking.
116      */
117     node = QLIST_FIRST(&ctx->aio_handlers);
118     while (node) {
119         AioHandler *tmp;
120
121         ctx->walking_handlers++;
122
123         if (node->pfd.revents && node->io_notify) {
124             node->pfd.revents = 0;
125             node->io_notify(node->e);
126
127             /* aio_notify() does not count as progress */
128             if (node->opaque != &ctx->notifier) {
129                 progress = true;
130             }
131         }
132
133         tmp = node;
134         node = QLIST_NEXT(node, node);
135
136         ctx->walking_handlers--;
137
138         if (!ctx->walking_handlers && tmp->deleted) {
139             QLIST_REMOVE(tmp, node);
140             g_free(tmp);
141         }
142     }
143
144     if (progress && !blocking) {
145         return true;
146     }
147
148     ctx->walking_handlers++;
149
150     /* fill fd sets */
151     count = 0;
152     QLIST_FOREACH(node, &ctx->aio_handlers, node) {
153         if (!node->deleted && node->io_notify) {
154             events[count++] = event_notifier_get_handle(node->e);
155         }
156     }
157
158     ctx->walking_handlers--;
159
160     /* early return if we only have the aio_notify() fd */
161     if (count == 1) {
162         return progress;
163     }
164
165     /* wait until next event */
166     while (count > 0) {
167         int timeout = blocking ? INFINITE : 0;
168         int ret = WaitForMultipleObjects(count, events, FALSE, timeout);
169
170         /* if we have any signaled events, dispatch event */
171         if ((DWORD) (ret - WAIT_OBJECT_0) >= count) {
172             break;
173         }
174
175         blocking = false;
176
177         /* we have to walk very carefully in case
178          * qemu_aio_set_fd_handler is called while we're walking */
179         node = QLIST_FIRST(&ctx->aio_handlers);
180         while (node) {
181             AioHandler *tmp;
182
183             ctx->walking_handlers++;
184
185             if (!node->deleted &&
186                 event_notifier_get_handle(node->e) == events[ret - WAIT_OBJECT_0] &&
187                 node->io_notify) {
188                 node->io_notify(node->e);
189
190                 /* aio_notify() does not count as progress */
191                 if (node->opaque != &ctx->notifier) {
192                     progress = true;
193                 }
194             }
195
196             tmp = node;
197             node = QLIST_NEXT(node, node);
198
199             ctx->walking_handlers--;
200
201             if (!ctx->walking_handlers && tmp->deleted) {
202                 QLIST_REMOVE(tmp, node);
203                 g_free(tmp);
204             }
205         }
206
207         /* Try again, but only call each handler once.  */
208         events[ret - WAIT_OBJECT_0] = events[--count];
209     }
210
211     return progress;
212 }