David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 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> |
David 'Digit' Turner | 1bb627c | 2010-11-18 16:10:45 +0100 | [diff] [blame] | 16 | #include <android/utils/panic.h> |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 17 | #include "qemu-common.h" |
David 'Digit' Turner | 7a78db7 | 2013-12-14 11:46:01 +0100 | [diff] [blame] | 18 | #include "qemu/timer.h" |
David 'Digit' Turner | e7216d8 | 2013-12-15 00:51:13 +0100 | [diff] [blame] | 19 | #include "sysemu/char.h" |
David 'Digit' Turner | cc330d4 | 2013-12-14 23:26:42 +0100 | [diff] [blame] | 20 | #include "android/sockets.h" /* for socket_set_nonblock() */ |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 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) |
David 'Digit' Turner | dcda949 | 2014-02-16 15:13:55 +0100 | [diff] [blame] | 37 | timer_del(tt); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 38 | else |
David 'Digit' Turner | dcda949 | 2014-02-16 15:13:55 +0100 | [diff] [blame] | 39 | timer_mod(tt, qemu_clock_get_ms(QEMU_CLOCK_HOST) + timeout_ms); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 40 | } |
| 41 | |
| 42 | static void |
| 43 | qlooptimer_startAbsolute(void* impl, Duration deadline_ms) |
| 44 | { |
| 45 | QEMUTimer* tt = impl; |
| 46 | if (deadline_ms == DURATION_INFINITE) |
David 'Digit' Turner | dcda949 | 2014-02-16 15:13:55 +0100 | [diff] [blame] | 47 | timer_del(tt); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 48 | else |
David 'Digit' Turner | dcda949 | 2014-02-16 15:13:55 +0100 | [diff] [blame] | 49 | timer_mod(tt, deadline_ms); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 50 | } |
| 51 | |
| 52 | static void |
| 53 | qlooptimer_stop(void* impl) |
| 54 | { |
| 55 | QEMUTimer* tt = impl; |
David 'Digit' Turner | dcda949 | 2014-02-16 15:13:55 +0100 | [diff] [blame] | 56 | timer_del(tt); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 57 | } |
| 58 | |
| 59 | static int |
| 60 | qlooptimer_isActive(void* impl) |
| 61 | { |
| 62 | QEMUTimer* tt = impl; |
David 'Digit' Turner | dcda949 | 2014-02-16 15:13:55 +0100 | [diff] [blame] | 63 | return timer_pending(tt); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 64 | } |
| 65 | |
| 66 | static void |
| 67 | qlooptimer_free(void* impl) |
| 68 | { |
| 69 | QEMUTimer* tt = impl; |
David 'Digit' Turner | dcda949 | 2014-02-16 15:13:55 +0100 | [diff] [blame] | 70 | timer_free(tt); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 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; |
David 'Digit' Turner | dcda949 | 2014-02-16 15:13:55 +0100 | [diff] [blame] | 88 | timer->impl = timer_new(QEMU_CLOCK_HOST, SCALE_MS, callback, opaque); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 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 | { |
David 'Digit' Turner | aa8236d | 2014-01-10 17:02:29 +0100 | [diff] [blame] | 143 | QLoopIo* io = g_malloc(sizeof(*io)); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 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 */ |
Vladimir Chtchetkine | e95660a | 2010-12-20 08:28:03 -0800 | [diff] [blame] | 202 | if (io->ready && (io->ready & wanted) == 0) { |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 203 | qloopio_removePending(io); |
Vladimir Chtchetkine | e95660a | 2010-12-20 08:28:03 -0800 | [diff] [blame] | 204 | } |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 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); |
Vladimir Chtchetkine | e95660a | 2010-12-20 08:28:03 -0800 | [diff] [blame] | 210 | io->wanted = wanted; |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 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; |
David 'Digit' Turner | aa8236d | 2014-01-10 17:02:29 +0100 | [diff] [blame] | 254 | g_free(io); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 255 | } |
| 256 | |
David 'Digit' Turner | a65e415 | 2011-04-28 16:29:57 +0200 | [diff] [blame] | 257 | static unsigned |
| 258 | qloopio_poll(void* impl) |
| 259 | { |
| 260 | QLoopIo* io = impl; |
| 261 | return io->ready; |
| 262 | } |
| 263 | |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 264 | static const LoopIoClass qlooper_io_class = { |
| 265 | qloopio_wantRead, |
| 266 | qloopio_wantWrite, |
| 267 | qloopio_dontWantRead, |
| 268 | qloopio_dontWantWrite, |
David 'Digit' Turner | a65e415 | 2011-04-28 16:29:57 +0200 | [diff] [blame] | 269 | qloopio_poll, |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 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; |
Vladimir Chtchetkine | e95660a | 2010-12-20 08:28:03 -0800 | [diff] [blame] | 325 | looper->io_pending = io; |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 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 | { |
David 'Digit' Turner | dcda949 | 2014-02-16 15:13:55 +0100 | [diff] [blame] | 373 | return qemu_clock_get_ms(QEMU_CLOCK_HOST); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 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 | |
David 'Digit' Turner | 1bb627c | 2010-11-18 16:10:45 +0100 | [diff] [blame] | 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 | |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 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); |
David 'Digit' Turner | aa8236d | 2014-01-10 17:02:29 +0100 | [diff] [blame] | 403 | g_free(looper); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 404 | } |
| 405 | |
| 406 | Looper* |
| 407 | looper_newCore(void) |
| 408 | { |
David 'Digit' Turner | aa8236d | 2014-01-10 17:02:29 +0100 | [diff] [blame] | 409 | QLooper* looper = g_malloc0(sizeof(*looper)); |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 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; |
David 'Digit' Turner | 1bb627c | 2010-11-18 16:10:45 +0100 | [diff] [blame] | 418 | looper->looper.run = qlooper_run; |
David 'Digit' Turner | 7a17b60 | 2010-11-17 17:58:29 +0100 | [diff] [blame] | 419 | looper->looper.forceQuit = qlooper_forceQuit; |
| 420 | looper->looper.destroy = qlooper_destroy; |
| 421 | |
| 422 | return &looper->looper; |
| 423 | } |