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 32typedef struct GLoopTimer GLoopTimer; 33typedef struct GLoopIo GLoopIo; 34typedef struct GLooper GLooper; 35 36struct GLoopTimer { 37 Duration deadline; 38 LoopTimerFunc callback; 39 void* opaque; 40 GLooper* looper; 41 GLoopTimer* activeNext; 42}; 43 44static Duration glooper_now(Looper* ll); 45 46static void glooper_addActiveTimer(GLooper* looper, GLoopTimer* timer); 47static void glooper_delActiveTimer(GLooper* looper, GLoopTimer* timer); 48static void glooper_addTimer(GLooper* looper, GLoopTimer* timer); 49static void glooper_delTimer(GLooper* looper, GLoopTimer* timer); 50 51static void 52glooptimer_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 61static void 62glooptimer_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 78static void 79glooptimer_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 90static int 91glooptimer_isActive(void* impl) 92{ 93 GLoopTimer* tt = impl; 94 return (tt->deadline != DURATION_INFINITE); 95} 96 97static void 98glooptimer_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 109static const LoopTimerClass glooptimer_class = { 110 glooptimer_startRelative, 111 glooptimer_startAbsolute, 112 glooptimer_stop, 113 glooptimer_isActive, 114 glooptimer_free 115}; 116 117static void 118glooper_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 146struct GLoopIo { 147 int fd; 148 LoopIoFunc callback; 149 void* opaque; 150 unsigned wanted; 151 unsigned ready; 152 GLooper* looper; 153}; 154 155static void glooper_delPendingIo(GLooper* looper, GLoopIo* io); 156static void glooper_addIo(GLooper* looper, GLoopIo* io); 157static void glooper_delIo(GLooper* looper, GLoopIo* io); 158static void glooper_modifyFd(GLooper* looper, int fd, int oldwanted, int newwanted); 159 160/* used to indicate that the set of wanted flags has changed */ 161static void 162gloopio_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 178static void 179gloopio_wantRead(void* impl) 180{ 181 GLoopIo* io = impl; 182 gloopio_modify(io, io->wanted | LOOP_IO_READ); 183} 184 185static void 186gloopio_wantWrite(void* impl) 187{ 188 GLoopIo* io = impl; 189 gloopio_modify(io, io->wanted | LOOP_IO_WRITE); 190} 191 192static void 193gloopio_dontWantRead(void* impl) 194{ 195 GLoopIo* io = impl; 196 gloopio_modify(io, io->wanted & ~LOOP_IO_READ); 197} 198 199static void 200gloopio_dontWantWrite(void* impl) 201{ 202 GLoopIo* io = impl; 203 gloopio_modify(io, io->wanted & ~LOOP_IO_WRITE); 204} 205 206static unsigned 207gloopio_poll(void* impl) 208{ 209 GLoopIo* io = impl; 210 return io->ready; 211} 212 213static void 214gloopio_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 224static LoopIoClass gloopio_class = { 225 gloopio_wantRead, 226 gloopio_wantWrite, 227 gloopio_dontWantRead, 228 gloopio_dontWantWrite, 229 gloopio_poll, 230 gloopio_free 231}; 232 233static void 234glooper_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 263struct 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 276static void 277glooper_addTimer(GLooper* looper, GLoopTimer* tt) 278{ 279 arefSet_add(looper->timers, tt); 280} 281 282static void 283glooper_delTimer(GLooper* looper, GLoopTimer* tt) 284{ 285 arefSet_del(looper->timers, tt); 286} 287 288static void 289glooper_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 303static void 304glooper_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 319static void 320glooper_addIo(GLooper* looper, GLoopIo* io) 321{ 322 arefSet_add(looper->ios, io); 323} 324 325static void 326glooper_delIo(GLooper* looper, GLoopIo* io) 327{ 328 arefSet_del(looper->ios, io); 329} 330 331static void 332glooper_delPendingIo(GLooper* looper, GLoopIo* io) 333{ 334 arefSet_del(looper->pendingIos, io); 335} 336 337static void 338glooper_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 349static Duration 350glooper_now(Looper* ll) 351{ 352 return iolooper_now(); 353} 354 355static void 356glooper_forceQuit(Looper* ll) 357{ 358 GLooper* looper = (GLooper*)ll; 359 looper->running = 0; 360} 361 362static int 363glooper_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 464static void 465glooper_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 481Looper* 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