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 32 static void 33 qlooptimer_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 42 static void 43 qlooptimer_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 52 static void 53 qlooptimer_stop(void* impl) 54 { 55 QEMUTimer* tt = impl; 56 timer_del(tt); 57 } 58 59 static int 60 qlooptimer_isActive(void* impl) 61 { 62 QEMUTimer* tt = impl; 63 return timer_pending(tt); 64 } 65 66 static void 67 qlooptimer_free(void* impl) 68 { 69 QEMUTimer* tt = impl; 70 timer_free(tt); 71 } 72 73 static const LoopTimerClass qlooptimer_class = { 74 qlooptimer_startRelative, 75 qlooptimer_startAbsolute, 76 qlooptimer_stop, 77 qlooptimer_isActive, 78 qlooptimer_free 79 }; 80 81 static void 82 qlooper_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 122 typedef struct QLoopIo QLoopIo; 123 124 typedef struct QLooper QLooper; 125 126 struct 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 137 static void qlooper_addIo(QLooper* looper, QLoopIo* io); 138 static void qlooper_delIo(QLooper* looper, QLoopIo* io); 139 140 static QLoopIo* 141 qloopio_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 158 static void qlooper_addPendingIo(QLooper* qlooper, QLoopIo* io); 159 static void qlooper_delPendingIo(QLooper* qlooper, QLoopIo* io); 160 161 static void 162 qloopio_removePending(QLoopIo* io) 163 { 164 if (io->ready != 0) { 165 qlooper_delPendingIo(io->looper, io); 166 io->ready = 0; 167 } 168 } 169 170 static void 171 qloopio_setReady(QLoopIo* io, unsigned flag) 172 { 173 if (io->ready == 0) { 174 qlooper_addPendingIo(io->looper, io); 175 } 176 io->ready |= flag; 177 } 178 179 static void 180 qloopio_handleRead(void* opaque) 181 { 182 QLoopIo* io = opaque; 183 qloopio_setReady(io, LOOP_IO_READ); 184 } 185 186 static void 187 qloopio_handleWrite(void* opaque) 188 { 189 QLoopIo* io = opaque; 190 qloopio_setReady(io, LOOP_IO_WRITE); 191 } 192 193 static void 194 qloopio_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 213 static void 214 qloopio_wantRead(void* impl) 215 { 216 QLoopIo* io = impl; 217 qloopio_modify(io, io->wanted | LOOP_IO_READ); 218 } 219 220 static void 221 qloopio_wantWrite(void* impl) 222 { 223 QLoopIo* io = impl; 224 qloopio_modify(io, io->wanted | LOOP_IO_WRITE); 225 } 226 227 static void 228 qloopio_dontWantRead(void* impl) 229 { 230 QLoopIo* io = impl; 231 qloopio_modify(io, io->wanted & ~LOOP_IO_READ); 232 } 233 234 static void 235 qloopio_dontWantWrite(void* impl) 236 { 237 QLoopIo* io = impl; 238 qloopio_modify(io, io->wanted & ~LOOP_IO_WRITE); 239 } 240 241 static void 242 qloopio_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 257 static unsigned 258 qloopio_poll(void* impl) 259 { 260 QLoopIo* io = impl; 261 return io->ready; 262 } 263 264 static 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 273 static void 274 qlooper_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 288 struct QLooper { 289 Looper looper; 290 QLoopIo* io_list; 291 QLoopIo* io_pending; 292 QEMUBH* io_bh; 293 }; 294 295 static void 296 qlooper_addIo(QLooper* looper, QLoopIo* io) 297 { 298 io->next = looper->io_list; 299 looper->io_list = io; 300 } 301 302 static void 303 qlooper_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 318 static void 319 qlooper_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 328 static void 329 qlooper_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 */ 349 static void 350 qlooper_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 370 static Duration 371 qlooper_now(Looper* ll) 372 { 373 return qemu_clock_get_ms(QEMU_CLOCK_HOST); 374 } 375 376 extern void qemu_system_shutdown_request(void); 377 378 static void 379 qlooper_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. */ 386 int 387 qlooper_run(Looper* ll, Duration deadline_ms) 388 { 389 APANIC("Trying to run the QEMU main event loop explicitely!"); 390 return EINVAL; 391 } 392 393 static void 394 qlooper_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 406 Looper* 407 looper_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