blob: d7e0b666cd9cdac0a35e5be94492bf007d6bf9f1 [file] [log] [blame]
David 'Digit' Turner7a17b602010-11-17 17:58:29 +01001/* Copyright (C) 2010 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
12
13/* Implement the Looper interface on top of the QEMU main event loop */
14
15#include <android/looper.h>
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +010016#include <android/utils/panic.h>
David 'Digit' Turner7a17b602010-11-17 17:58:29 +010017#include "qemu-common.h"
David 'Digit' Turner7a78db72013-12-14 11:46:01 +010018#include "qemu/timer.h"
David 'Digit' Turnere7216d82013-12-15 00:51:13 +010019#include "sysemu/char.h"
David 'Digit' Turnercc330d42013-12-14 23:26:42 +010020#include "android/sockets.h" /* for socket_set_nonblock() */
David 'Digit' Turner7a17b602010-11-17 17:58:29 +010021
22/**********************************************************************
23 **********************************************************************
24 *****
25 ***** T I M E R S
26 *****
27 **********************************************************************
28 **********************************************************************/
29
30/* Model a timer simple as a QEMUTimer for the host_clock */
31
32static void
33qlooptimer_startRelative(void* impl, Duration timeout_ms)
34{
35 QEMUTimer* tt = impl;
36 if (timeout_ms == DURATION_INFINITE)
David 'Digit' Turnerdcda9492014-02-16 15:13:55 +010037 timer_del(tt);
David 'Digit' Turner7a17b602010-11-17 17:58:29 +010038 else
David 'Digit' Turnerdcda9492014-02-16 15:13:55 +010039 timer_mod(tt, qemu_clock_get_ms(QEMU_CLOCK_HOST) + timeout_ms);
David 'Digit' Turner7a17b602010-11-17 17:58:29 +010040}
41
42static void
43qlooptimer_startAbsolute(void* impl, Duration deadline_ms)
44{
45 QEMUTimer* tt = impl;
46 if (deadline_ms == DURATION_INFINITE)
David 'Digit' Turnerdcda9492014-02-16 15:13:55 +010047 timer_del(tt);
David 'Digit' Turner7a17b602010-11-17 17:58:29 +010048 else
David 'Digit' Turnerdcda9492014-02-16 15:13:55 +010049 timer_mod(tt, deadline_ms);
David 'Digit' Turner7a17b602010-11-17 17:58:29 +010050}
51
52static void
53qlooptimer_stop(void* impl)
54{
55 QEMUTimer* tt = impl;
David 'Digit' Turnerdcda9492014-02-16 15:13:55 +010056 timer_del(tt);
David 'Digit' Turner7a17b602010-11-17 17:58:29 +010057}
58
59static int
60qlooptimer_isActive(void* impl)
61{
62 QEMUTimer* tt = impl;
David 'Digit' Turnerdcda9492014-02-16 15:13:55 +010063 return timer_pending(tt);
David 'Digit' Turner7a17b602010-11-17 17:58:29 +010064}
65
66static void
67qlooptimer_free(void* impl)
68{
69 QEMUTimer* tt = impl;
David 'Digit' Turnerdcda9492014-02-16 15:13:55 +010070 timer_free(tt);
David 'Digit' Turner7a17b602010-11-17 17:58:29 +010071}
72
73static const LoopTimerClass qlooptimer_class = {
74 qlooptimer_startRelative,
75 qlooptimer_startAbsolute,
76 qlooptimer_stop,
77 qlooptimer_isActive,
78 qlooptimer_free
79};
80
81static void
82qlooper_timer_init(Looper* looper,
83 LoopTimer* timer,
84 LoopTimerFunc callback,
85 void* opaque)
86{
87 timer->clazz = (LoopTimerClass*) &qlooptimer_class;
David 'Digit' Turnerdcda9492014-02-16 15:13:55 +010088 timer->impl = timer_new(QEMU_CLOCK_HOST, SCALE_MS, callback, opaque);
David 'Digit' Turner7a17b602010-11-17 17:58:29 +010089}
90
91/**********************************************************************
92 **********************************************************************
93 *****
94 ***** F I L E D E S C R I P T O R S
95 *****
96 **********************************************************************
97 **********************************************************************/
98
99/* Modeling the LoopIo is a bit more complex because the main event loop
100 * will call different functions for read and write readiness, while our
101 * users expect a single call with a mask of ready events.
102 *
103 * Since the QEMU main event loop looks like the following:
104 *
105 * 1/ perform select()
106 * 2/ for each file descriptor:
107 * if readReady:
108 * call readHandler()
109 * if writeReady:
110 * call writeHandler()
111 * 3/ run timers
112 * 4/ run bottom-half handlers
113 *
114 * We're going to provide simple read and write handlers that only mark
115 * the file descriptor for readiness, and put it on a "pending list".
116 *
117 * Then, we're going to schedule a bottom-half handler when such a pending
118 * i/o event occurs, in order to call the user callback with the correct
119 * flags.
120 */
121
122typedef struct QLoopIo QLoopIo;
123
124typedef struct QLooper QLooper;
125
126struct QLoopIo {
127 int fd;
128 LoopIoFunc user_callback;
129 void* user_opaque;
130 unsigned wanted;
131 unsigned ready;
132 QLooper* looper;
133 QLoopIo* pendingNext;
134 QLoopIo* next;
135};
136
137static void qlooper_addIo(QLooper* looper, QLoopIo* io);
138static void qlooper_delIo(QLooper* looper, QLoopIo* io);
139
140static QLoopIo*
141qloopio_new(int fd, LoopIoFunc callback, void* opaque, QLooper* qlooper)
142{
David 'Digit' Turneraa8236d2014-01-10 17:02:29 +0100143 QLoopIo* io = g_malloc(sizeof(*io));
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100144
145 io->fd = fd;
146 io->user_callback = callback;
147 io->user_opaque = opaque;
148 io->wanted = 0;
149 io->ready = 0;
150 io->looper = qlooper;
151 io->pendingNext = NULL;
152
153 qlooper_addIo(qlooper, io);
154
155 return io;
156}
157
158static void qlooper_addPendingIo(QLooper* qlooper, QLoopIo* io);
159static void qlooper_delPendingIo(QLooper* qlooper, QLoopIo* io);
160
161static void
162qloopio_removePending(QLoopIo* io)
163{
164 if (io->ready != 0) {
165 qlooper_delPendingIo(io->looper, io);
166 io->ready = 0;
167 }
168}
169
170static void
171qloopio_setReady(QLoopIo* io, unsigned flag)
172{
173 if (io->ready == 0) {
174 qlooper_addPendingIo(io->looper, io);
175 }
176 io->ready |= flag;
177}
178
179static void
180qloopio_handleRead(void* opaque)
181{
182 QLoopIo* io = opaque;
183 qloopio_setReady(io, LOOP_IO_READ);
184}
185
186static void
187qloopio_handleWrite(void* opaque)
188{
189 QLoopIo* io = opaque;
190 qloopio_setReady(io, LOOP_IO_WRITE);
191}
192
193static void
194qloopio_modify(QLoopIo* io, unsigned wanted)
195{
196 /* no change, don't bother */
197 if (wanted == io->wanted)
198 return;
199
200 /* if we're pending, but the new mask doesn't care about
201 * out state, remove from pending list */
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800202 if (io->ready && (io->ready & wanted) == 0) {
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100203 qloopio_removePending(io);
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800204 }
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100205
206 /* recompute read/write handlers for QEMU */
207 IOHandler* fd_read = (wanted & LOOP_IO_READ) ? qloopio_handleRead : NULL;
208 IOHandler* fd_write = (wanted & LOOP_IO_WRITE) ? qloopio_handleWrite : NULL;
209 qemu_set_fd_handler(io->fd, fd_read, fd_write, io);
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800210 io->wanted = wanted;
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100211}
212
213static void
214qloopio_wantRead(void* impl)
215{
216 QLoopIo* io = impl;
217 qloopio_modify(io, io->wanted | LOOP_IO_READ);
218}
219
220static void
221qloopio_wantWrite(void* impl)
222{
223 QLoopIo* io = impl;
224 qloopio_modify(io, io->wanted | LOOP_IO_WRITE);
225}
226
227static void
228qloopio_dontWantRead(void* impl)
229{
230 QLoopIo* io = impl;
231 qloopio_modify(io, io->wanted & ~LOOP_IO_READ);
232}
233
234static void
235qloopio_dontWantWrite(void* impl)
236{
237 QLoopIo* io = impl;
238 qloopio_modify(io, io->wanted & ~LOOP_IO_WRITE);
239}
240
241static void
242qloopio_free(void* impl)
243{
244 QLoopIo* io = impl;
245 if (io->ready)
246 qloopio_removePending(io);
247
248 /* remove from global list */
249 qlooper_delIo(io->looper, io);
250
251 /* make QEMU forget about this fd */
252 qemu_set_fd_handler(io->fd, NULL, NULL, NULL);
253 io->fd = -1;
David 'Digit' Turneraa8236d2014-01-10 17:02:29 +0100254 g_free(io);
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100255}
256
David 'Digit' Turnera65e4152011-04-28 16:29:57 +0200257static unsigned
258qloopio_poll(void* impl)
259{
260 QLoopIo* io = impl;
261 return io->ready;
262}
263
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100264static const LoopIoClass qlooper_io_class = {
265 qloopio_wantRead,
266 qloopio_wantWrite,
267 qloopio_dontWantRead,
268 qloopio_dontWantWrite,
David 'Digit' Turnera65e4152011-04-28 16:29:57 +0200269 qloopio_poll,
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100270 qloopio_free
271};
272
273static void
274qlooper_io_init(Looper* looper,
275 LoopIo* loopio,
276 int fd,
277 LoopIoFunc callback,
278 void* opaque)
279{
280 QLoopIo* io = qloopio_new(fd, callback, opaque, (QLooper*)looper);
281
282 socket_set_nonblock(fd);
283
284 loopio->clazz = (LoopIoClass*) &qlooper_io_class;
285 loopio->impl = io;
286}
287
288struct QLooper {
289 Looper looper;
290 QLoopIo* io_list;
291 QLoopIo* io_pending;
292 QEMUBH* io_bh;
293};
294
295static void
296qlooper_addIo(QLooper* looper, QLoopIo* io)
297{
298 io->next = looper->io_list;
299 looper->io_list = io;
300}
301
302static void
303qlooper_delIo(QLooper* looper, QLoopIo* io)
304{
305 QLoopIo** pnode = &looper->io_list;
306 for (;;) {
307 if (*pnode == NULL)
308 break;
309 if (*pnode == io) {
310 *pnode = io->next;
311 io->next = NULL;
312 break;
313 }
314 pnode = &(*pnode)->next;
315 }
316}
317
318static void
319qlooper_addPendingIo(QLooper* looper, QLoopIo* io)
320{
321 if (looper->io_pending == NULL) {
322 qemu_bh_schedule(looper->io_bh);
323 }
324 io->pendingNext = looper->io_pending;
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800325 looper->io_pending = io;
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100326}
327
328static void
329qlooper_delPendingIo(QLooper* looper, QLoopIo* io)
330{
331 QLoopIo** pnode = &looper->io_pending;
332 for (;;) {
333 if (*pnode == NULL)
334 break;
335 if (*pnode == io) {
336 *pnode = io->pendingNext;
337 break;
338 }
339 pnode = &(*pnode)->pendingNext;
340 }
341 io->pendingNext = NULL;
342}
343
344/* This function is called by the main event loop when pending i/o
345 * events were registered with qlooper_addPendingIo(). It will parse
346 * the list of pending QLoopIo and call the user callback with the
347 * appropriate flags.
348 */
349static void
350qlooper_handle_io_bh(void* opaque)
351{
352 QLooper* looper = opaque;
353 QLoopIo* io;
354
355 while ((io = looper->io_pending) != NULL) {
356 unsigned ready;
357 /* extract from list */
358 looper->io_pending = io->pendingNext;
359 io->pendingNext = NULL;
360 /* call the user callback, clear io->ready before to
361 * indicate that the item is not on the pending list
362 * anymore.
363 */
364 ready = io->ready;
365 io->ready = 0;
366 io->user_callback(io->user_opaque, io->fd, ready);
367 }
368}
369
370static Duration
371qlooper_now(Looper* ll)
372{
David 'Digit' Turnerdcda9492014-02-16 15:13:55 +0100373 return qemu_clock_get_ms(QEMU_CLOCK_HOST);
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100374}
375
376extern void qemu_system_shutdown_request(void);
377
378static void
379qlooper_forceQuit(Looper* ll)
380{
381 qemu_system_shutdown_request();
382}
383
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +0100384/* The user cannot call looper_run on the core event loop, because it
385 * is started by qemu_main() explicitely instead, so just panic. */
386int
387qlooper_run(Looper* ll, Duration deadline_ms)
388{
389 APANIC("Trying to run the QEMU main event loop explicitely!");
390 return EINVAL;
391}
392
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100393static void
394qlooper_destroy(Looper* ll)
395{
396 QLooper* looper = (QLooper*)ll;
397 QLoopIo* io;
398
399 while ((io = looper->io_list) != NULL)
400 qloopio_free(io);
401
402 qemu_bh_delete(looper->io_bh);
David 'Digit' Turneraa8236d2014-01-10 17:02:29 +0100403 g_free(looper);
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100404}
405
406Looper*
407looper_newCore(void)
408{
David 'Digit' Turneraa8236d2014-01-10 17:02:29 +0100409 QLooper* looper = g_malloc0(sizeof(*looper));
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100410
411 looper->io_list = NULL;
412 looper->io_pending = NULL;
413 looper->io_bh = qemu_bh_new(qlooper_handle_io_bh, looper);
414
415 looper->looper.now = qlooper_now;
416 looper->looper.timer_init = qlooper_timer_init;
417 looper->looper.io_init = qlooper_io_init;
David 'Digit' Turner1bb627c2010-11-18 16:10:45 +0100418 looper->looper.run = qlooper_run;
David 'Digit' Turner7a17b602010-11-17 17:58:29 +0100419 looper->looper.forceQuit = qlooper_forceQuit;
420 looper->looper.destroy = qlooper_destroy;
421
422 return &looper->looper;
423}