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 #include "android/utils/assert.h" 14 #include "android/utils/reflist.h" 15 #include "android/utils/refset.h" 16 #include "android/utils/system.h" 17 #include "android/looper.h" 18 #include "iolooper.h" 19 #include "sockets.h" 20 #include <inttypes.h> 21 #include <limits.h> 22 #include <errno.h> 23 24 /********************************************************************** 25 ********************************************************************** 26 ***** 27 ***** T I M E R S 28 ***** 29 ********************************************************************** 30 **********************************************************************/ 31 32 typedef struct GLoopTimer GLoopTimer; 33 typedef struct GLoopIo GLoopIo; 34 typedef struct GLooper GLooper; 35 36 struct GLoopTimer { 37 Duration deadline; 38 LoopTimerFunc callback; 39 void* opaque; 40 GLooper* looper; 41 GLoopTimer* activeNext; 42 }; 43 44 static Duration glooper_now(Looper* ll); 45 46 static void glooper_addActiveTimer(GLooper* looper, GLoopTimer* timer); 47 static void glooper_delActiveTimer(GLooper* looper, GLoopTimer* timer); 48 static void glooper_addTimer(GLooper* looper, GLoopTimer* timer); 49 static void glooper_delTimer(GLooper* looper, GLoopTimer* timer); 50 51 static void 52 glooptimer_stop(void* impl) 53 { 54 GLoopTimer* tt = impl; 55 if (tt->deadline != DURATION_INFINITE) { 56 glooper_delActiveTimer(tt->looper, tt); 57 tt->deadline = DURATION_INFINITE; 58 } 59 } 60 61 static void 62 glooptimer_startAbsolute(void* impl, Duration deadline_ms) 63 { 64 GLoopTimer* tt = impl; 65 66 /* Stop the timer if it was active */ 67 if (tt->deadline != DURATION_INFINITE) 68 glooptimer_stop(tt); 69 70 /* Another way to stop a timer */ 71 if (deadline_ms == DURATION_INFINITE) 72 return; 73 74 tt->deadline = deadline_ms; 75 glooper_addActiveTimer(tt->looper, tt); 76 } 77 78 static void 79 glooptimer_startRelative(void* impl, Duration timeout_ms) 80 { 81 GLoopTimer* tt = impl; 82 83 if (timeout_ms == DURATION_INFINITE) { /* another way to stop the timer */ 84 glooptimer_stop(tt); 85 } else { 86 glooptimer_startAbsolute(tt, timeout_ms + glooper_now((Looper*)tt->looper)); 87 } 88 } 89 90 static int 91 glooptimer_isActive(void* impl) 92 { 93 GLoopTimer* tt = impl; 94 return (tt->deadline != DURATION_INFINITE); 95 } 96 97 static void 98 glooptimer_free(void* impl) 99 { 100 GLoopTimer* tt = impl; 101 102 if (tt->deadline != DURATION_INFINITE) 103 glooptimer_stop(tt); 104 105 glooper_delTimer(tt->looper, tt); 106 AFREE(tt); 107 } 108 109 static const LoopTimerClass glooptimer_class = { 110 glooptimer_startRelative, 111 glooptimer_startAbsolute, 112 glooptimer_stop, 113 glooptimer_isActive, 114 glooptimer_free 115 }; 116 117 static void 118 glooper_timer_init(Looper* looper, 119 LoopTimer* timer, 120 LoopTimerFunc callback, 121 void* opaque) 122 { 123 GLoopTimer* tt; 124 125 ANEW0(tt); 126 127 tt->deadline = DURATION_INFINITE; 128 tt->callback = callback; 129 tt->opaque = opaque; 130 tt->looper = (GLooper*) looper; 131 132 glooper_addTimer(tt->looper, tt); 133 134 timer->impl = tt; 135 timer->clazz = (LoopTimerClass*) &glooptimer_class; 136 } 137 138 /********************************************************************** 139 ********************************************************************** 140 ***** 141 ***** I / O 142 ***** 143 ********************************************************************** 144 **********************************************************************/ 145 146 struct GLoopIo { 147 int fd; 148 LoopIoFunc callback; 149 void* opaque; 150 unsigned wanted; 151 unsigned ready; 152 GLooper* looper; 153 }; 154 155 static void glooper_delPendingIo(GLooper* looper, GLoopIo* io); 156 static void glooper_addIo(GLooper* looper, GLoopIo* io); 157 static void glooper_delIo(GLooper* looper, GLoopIo* io); 158 static void glooper_modifyFd(GLooper* looper, int fd, int oldwanted, int newwanted); 159 160 /* used to indicate that the set of wanted flags has changed */ 161 static void 162 gloopio_modify(GLoopIo* io, unsigned wanted) 163 { 164 /* If nothing changed, return */ 165 if (io->wanted == wanted) 166 return; 167 168 /* If we are pending, and we're not interested by the 169 * current ready flags, remove from list */ 170 if (io->ready != 0 && (io->ready & wanted) == 0) { 171 glooper_delPendingIo(io->looper, io); 172 } 173 io->ready &= wanted; 174 glooper_modifyFd(io->looper, io->fd, io->wanted, wanted); 175 io->wanted = wanted; 176 } 177 178 static void 179 gloopio_wantRead(void* impl) 180 { 181 GLoopIo* io = impl; 182 gloopio_modify(io, io->wanted | LOOP_IO_READ); 183 } 184 185 static void 186 gloopio_wantWrite(void* impl) 187 { 188 GLoopIo* io = impl; 189 gloopio_modify(io, io->wanted | LOOP_IO_WRITE); 190 } 191 192 static void 193 gloopio_dontWantRead(void* impl) 194 { 195 GLoopIo* io = impl; 196 gloopio_modify(io, io->wanted & ~LOOP_IO_READ); 197 } 198 199 static void 200 gloopio_dontWantWrite(void* impl) 201 { 202 GLoopIo* io = impl; 203 gloopio_modify(io, io->wanted & ~LOOP_IO_WRITE); 204 } 205 206 static unsigned 207 gloopio_poll(void* impl) 208 { 209 GLoopIo* io = impl; 210 return io->ready; 211 } 212 213 static void 214 gloopio_free(void* impl) 215 { 216 GLoopIo* io = impl; 217 if (io->ready != 0) 218 glooper_delPendingIo(io->looper, io); 219 220 glooper_delIo(io->looper, io); 221 AFREE(io); 222 } 223 224 static LoopIoClass gloopio_class = { 225 gloopio_wantRead, 226 gloopio_wantWrite, 227 gloopio_dontWantRead, 228 gloopio_dontWantWrite, 229 gloopio_poll, 230 gloopio_free 231 }; 232 233 static void 234 glooper_io_init(Looper* looper, LoopIo* user, int fd, LoopIoFunc callback, void* opaque) 235 { 236 GLooper* gg = (GLooper*)looper; 237 GLoopIo* io; 238 239 ANEW0(io); 240 io->fd = fd; 241 io->callback = callback; 242 io->opaque = opaque; 243 io->looper = (GLooper*) looper; 244 io->wanted = 0; 245 io->ready = 0; 246 247 socket_set_nonblock(fd); 248 249 glooper_addIo(gg, io); 250 251 user->impl = io; 252 user->clazz = (LoopIoClass*) &gloopio_class; 253 } 254 255 /********************************************************************** 256 ********************************************************************** 257 ***** 258 ***** L O O P E R 259 ***** 260 ********************************************************************** 261 **********************************************************************/ 262 263 struct GLooper { 264 Looper looper; 265 ARefSet timers[1]; /* set of all timers */ 266 GLoopTimer* activeTimers; /* sorted list of active timers */ 267 268 ARefSet ios[1]; /* set of all i/o waiters */ 269 ARefSet pendingIos[1]; /* list of pending i/o waiters */ 270 int numActiveIos; /* number of active LoopIo objects */ 271 272 IoLooper* iolooper; 273 int running; 274 }; 275 276 static void 277 glooper_addTimer(GLooper* looper, GLoopTimer* tt) 278 { 279 arefSet_add(looper->timers, tt); 280 } 281 282 static void 283 glooper_delTimer(GLooper* looper, GLoopTimer* tt) 284 { 285 arefSet_del(looper->timers, tt); 286 } 287 288 static void 289 glooper_addActiveTimer(GLooper* looper, GLoopTimer* tt) 290 { 291 Duration deadline = tt->deadline; 292 GLoopTimer** pnode = &looper->activeTimers; 293 for (;;) { 294 GLoopTimer* node = *pnode; 295 if (node == NULL || node->deadline > deadline) 296 break; 297 pnode = &node->activeNext; 298 } 299 tt->activeNext = *pnode; 300 *pnode = tt; 301 } 302 303 static void 304 glooper_delActiveTimer(GLooper* looper, GLoopTimer* tt) 305 { 306 GLoopTimer** pnode = &looper->activeTimers; 307 for (;;) { 308 if (*pnode == NULL) 309 break; 310 if (*pnode == tt) { 311 *pnode = tt->activeNext; 312 tt->activeNext = NULL; 313 break; 314 } 315 pnode = &(*pnode)->activeNext; 316 } 317 } 318 319 static void 320 glooper_addIo(GLooper* looper, GLoopIo* io) 321 { 322 arefSet_add(looper->ios, io); 323 } 324 325 static void 326 glooper_delIo(GLooper* looper, GLoopIo* io) 327 { 328 arefSet_del(looper->ios, io); 329 } 330 331 static void 332 glooper_delPendingIo(GLooper* looper, GLoopIo* io) 333 { 334 arefSet_del(looper->pendingIos, io); 335 } 336 337 static void 338 glooper_modifyFd(GLooper* looper, int fd, int oldWanted, int newWanted) 339 { 340 if (oldWanted == 0 && newWanted != 0) 341 looper->numActiveIos += 1; 342 343 if (oldWanted != 0 && newWanted == 0) 344 looper->numActiveIos -= 1; 345 346 iolooper_modify(looper->iolooper, fd, oldWanted, newWanted); 347 } 348 349 static Duration 350 glooper_now(Looper* ll) 351 { 352 return iolooper_now(); 353 } 354 355 static void 356 glooper_forceQuit(Looper* ll) 357 { 358 GLooper* looper = (GLooper*)ll; 359 looper->running = 0; 360 } 361 362 static int 363 glooper_run(Looper* ll, Duration loop_deadline_ms) 364 { 365 GLooper* looper = (GLooper*) ll; 366 IoLooper* iol = looper->iolooper; 367 368 looper->running = 1; 369 370 while (looper->running) 371 { 372 int ret; 373 374 /* Exit prematurely if we detect that we don't have any active timer 375 * and no active LoopIo 376 */ 377 if (looper->numActiveIos == 0 && looper->activeTimers == NULL) 378 return EWOULDBLOCK; 379 380 /* First, compute next deadline */ 381 Duration deadline = DURATION_INFINITE; 382 383 if (looper->activeTimers != NULL) 384 deadline = looper->activeTimers->deadline; 385 386 if (deadline > loop_deadline_ms) 387 deadline = loop_deadline_ms; 388 389 ret = iolooper_wait_absolute(iol, deadline); 390 if (ret < 0) { /* error, force stop ! */ 391 break; 392 } 393 if (ret > 0) { 394 unsigned ready; 395 GLoopIo* io; 396 397 /* Add io waiters to the pending list */ 398 AREFSET_FOREACH(looper->ios, io, { 399 if (io->wanted == 0) 400 continue; 401 402 ready = 0; 403 404 if (iolooper_is_read(iol, io->fd)) 405 ready |= LOOP_IO_READ; 406 407 if (iolooper_is_write(iol, io->fd)) 408 ready |= LOOP_IO_WRITE; 409 410 io->ready = ready; 411 if (ready != 0) { 412 arefSet_add(looper->pendingIos, io); 413 } 414 }); 415 } 416 417 /* Do we have any expired timers here ? */ 418 GLoopTimer* pendingTimers = NULL; 419 GLoopTimer** pendingLastP = &pendingTimers; 420 421 deadline = iolooper_now(); 422 for (;;) { 423 GLoopTimer* timer = looper->activeTimers; 424 if (timer == NULL || timer->deadline > deadline) 425 break; 426 427 /* remove from active list, and append to pending one */ 428 timer->deadline = DURATION_INFINITE; 429 looper->activeTimers = timer->activeNext; 430 431 *pendingLastP = timer; 432 timer->activeNext = NULL; 433 pendingLastP = &timer->activeNext; 434 } 435 436 /* Fire the pending timers, if any. We do that in a separate 437 * step because the callbacks could modify the active list 438 * by starting/stopping other timers. 439 */ 440 { 441 GLoopTimer* timer; 442 while ((timer = pendingTimers) != NULL) { 443 pendingTimers = timer->activeNext; 444 timer->activeNext = NULL; 445 timer->callback(timer->opaque); 446 } 447 } 448 449 /* Now fire the pending ios */ 450 { 451 GLoopIo* io; 452 AREFSET_FOREACH(looper->pendingIos,io,{ 453 io->callback(io->opaque,io->fd,io->ready); 454 }); 455 arefSet_clear(looper->pendingIos); 456 } 457 458 if (deadline > loop_deadline_ms) 459 return ETIMEDOUT; 460 } 461 return 0; 462 } 463 464 static void 465 glooper_free(Looper* ll) 466 { 467 GLooper* looper = (GLooper*)ll; 468 469 arefSet_done(looper->timers); 470 looper->activeTimers = NULL; 471 472 arefSet_done(looper->ios); 473 arefSet_done(looper->pendingIos); 474 475 iolooper_free(looper->iolooper); 476 looper->iolooper = NULL; 477 478 AFREE(looper); 479 } 480 481 Looper* looper_newGeneric(void) 482 { 483 GLooper* looper; 484 485 ANEW0(looper); 486 487 looper->iolooper = iolooper_new(); 488 489 looper->looper.now = glooper_now; 490 looper->looper.timer_init = glooper_timer_init; 491 looper->looper.io_init = glooper_io_init; 492 looper->looper.run = glooper_run; 493 looper->looper.forceQuit = glooper_forceQuit; 494 looper->looper.destroy = glooper_free; 495 496 /* Our implementation depends on these values being equal */ 497 AASSERT_INT(LOOP_IO_READ, IOLOOPER_READ); 498 AASSERT_INT(LOOP_IO_WRITE, IOLOOPER_WRITE); 499 500 return &looper->looper; 501 } 502