1/* 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> 16#include <android/utils/panic.h> 17#include "qemu-common.h" 18#include "qemu/timer.h" 19#include "sysemu/char.h" 20#include "android/sockets.h" /* for socket_set_nonblock() */ 21 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) 37 timer_del(tt); 38 else 39 timer_mod(tt, qemu_clock_get_ms(QEMU_CLOCK_HOST) + timeout_ms); 40} 41 42static void 43qlooptimer_startAbsolute(void* impl, Duration deadline_ms) 44{ 45 QEMUTimer* tt = impl; 46 if (deadline_ms == DURATION_INFINITE) 47 timer_del(tt); 48 else 49 timer_mod(tt, deadline_ms); 50} 51 52static void 53qlooptimer_stop(void* impl) 54{ 55 QEMUTimer* tt = impl; 56 timer_del(tt); 57} 58 59static int 60qlooptimer_isActive(void* impl) 61{ 62 QEMUTimer* tt = impl; 63 return timer_pending(tt); 64} 65 66static void 67qlooptimer_free(void* impl) 68{ 69 QEMUTimer* tt = impl; 70 timer_free(tt); 71} 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; 88 timer->impl = timer_new(QEMU_CLOCK_HOST, SCALE_MS, callback, opaque); 89} 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{ 143 QLoopIo* io = g_malloc(sizeof(*io)); 144 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 */ 202 if (io->ready && (io->ready & wanted) == 0) { 203 qloopio_removePending(io); 204 } 205 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); 210 io->wanted = wanted; 211} 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; 254 g_free(io); 255} 256 257static unsigned 258qloopio_poll(void* impl) 259{ 260 QLoopIo* io = impl; 261 return io->ready; 262} 263 264static const LoopIoClass qlooper_io_class = { 265 qloopio_wantRead, 266 qloopio_wantWrite, 267 qloopio_dontWantRead, 268 qloopio_dontWantWrite, 269 qloopio_poll, 270 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; 325 looper->io_pending = io; 326} 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{ 373 return qemu_clock_get_ms(QEMU_CLOCK_HOST); 374} 375 376extern void qemu_system_shutdown_request(void); 377 378static void 379qlooper_forceQuit(Looper* ll) 380{ 381 qemu_system_shutdown_request(); 382} 383 384/* 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 393static 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); 403 g_free(looper); 404} 405 406Looper* 407looper_newCore(void) 408{ 409 QLooper* looper = g_malloc0(sizeof(*looper)); 410 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; 418 looper->looper.run = qlooper_run; 419 looper->looper.forceQuit = qlooper_forceQuit; 420 looper->looper.destroy = qlooper_destroy; 421 422 return &looper->looper; 423} 424