blob: c8df86d015b55844e47ba269871d0fcd4896e992 [file] [log] [blame]
/* Copyright (C) 2010 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
#include "android/utils/assert.h"
#include "android/utils/reflist.h"
#include "android/utils/refset.h"
#include "android/utils/system.h"
#include "android/looper.h"
#include "android/iolooper.h"
#include "android/sockets.h"
#include <inttypes.h>
#include <limits.h>
#include <errno.h>
/**********************************************************************
**********************************************************************
*****
***** T I M E R S
*****
**********************************************************************
**********************************************************************/
typedef struct GLoopTimer GLoopTimer;
typedef struct GLoopIo GLoopIo;
typedef struct GLooper GLooper;
struct GLoopTimer {
Duration deadline;
LoopTimerFunc callback;
void* opaque;
GLooper* looper;
GLoopTimer* activeNext;
};
static Duration glooper_now(Looper* ll);
static void glooper_addActiveTimer(GLooper* looper, GLoopTimer* timer);
static void glooper_delActiveTimer(GLooper* looper, GLoopTimer* timer);
static void glooper_addTimer(GLooper* looper, GLoopTimer* timer);
static void glooper_delTimer(GLooper* looper, GLoopTimer* timer);
static void
glooptimer_stop(void* impl)
{
GLoopTimer* tt = impl;
if (tt->deadline != DURATION_INFINITE) {
glooper_delActiveTimer(tt->looper, tt);
tt->deadline = DURATION_INFINITE;
}
}
static void
glooptimer_startAbsolute(void* impl, Duration deadline_ms)
{
GLoopTimer* tt = impl;
/* Stop the timer if it was active */
if (tt->deadline != DURATION_INFINITE)
glooptimer_stop(tt);
/* Another way to stop a timer */
if (deadline_ms == DURATION_INFINITE)
return;
tt->deadline = deadline_ms;
glooper_addActiveTimer(tt->looper, tt);
}
static void
glooptimer_startRelative(void* impl, Duration timeout_ms)
{
GLoopTimer* tt = impl;
if (timeout_ms == DURATION_INFINITE) { /* another way to stop the timer */
glooptimer_stop(tt);
} else {
glooptimer_startAbsolute(tt, timeout_ms + glooper_now((Looper*)tt->looper));
}
}
static int
glooptimer_isActive(void* impl)
{
GLoopTimer* tt = impl;
return (tt->deadline != DURATION_INFINITE);
}
static void
glooptimer_free(void* impl)
{
GLoopTimer* tt = impl;
if (tt->deadline != DURATION_INFINITE)
glooptimer_stop(tt);
glooper_delTimer(tt->looper, tt);
AFREE(tt);
}
static const LoopTimerClass glooptimer_class = {
glooptimer_startRelative,
glooptimer_startAbsolute,
glooptimer_stop,
glooptimer_isActive,
glooptimer_free
};
static void
glooper_timer_init(Looper* looper,
LoopTimer* timer,
LoopTimerFunc callback,
void* opaque)
{
GLoopTimer* tt;
ANEW0(tt);
tt->deadline = DURATION_INFINITE;
tt->callback = callback;
tt->opaque = opaque;
tt->looper = (GLooper*) looper;
glooper_addTimer(tt->looper, tt);
timer->impl = tt;
timer->clazz = (LoopTimerClass*) &glooptimer_class;
}
/**********************************************************************
**********************************************************************
*****
***** I / O
*****
**********************************************************************
**********************************************************************/
struct GLoopIo {
int fd;
LoopIoFunc callback;
void* opaque;
unsigned wanted;
unsigned ready;
GLooper* looper;
};
static void glooper_delPendingIo(GLooper* looper, GLoopIo* io);
static void glooper_addIo(GLooper* looper, GLoopIo* io);
static void glooper_delIo(GLooper* looper, GLoopIo* io);
static void glooper_modifyFd(GLooper* looper, int fd, int oldwanted, int newwanted);
/* used to indicate that the set of wanted flags has changed */
static void
gloopio_modify(GLoopIo* io, unsigned wanted)
{
/* If nothing changed, return */
if (io->wanted == wanted)
return;
/* If we are pending, and we're not interested by the
* current ready flags, remove from list */
if (io->ready != 0 && (io->ready & wanted) == 0) {
glooper_delPendingIo(io->looper, io);
}
io->ready &= wanted;
glooper_modifyFd(io->looper, io->fd, io->wanted, wanted);
io->wanted = wanted;
}
static void
gloopio_wantRead(void* impl)
{
GLoopIo* io = impl;
gloopio_modify(io, io->wanted | LOOP_IO_READ);
}
static void
gloopio_wantWrite(void* impl)
{
GLoopIo* io = impl;
gloopio_modify(io, io->wanted | LOOP_IO_WRITE);
}
static void
gloopio_dontWantRead(void* impl)
{
GLoopIo* io = impl;
gloopio_modify(io, io->wanted & ~LOOP_IO_READ);
}
static void
gloopio_dontWantWrite(void* impl)
{
GLoopIo* io = impl;
gloopio_modify(io, io->wanted & ~LOOP_IO_WRITE);
}
static unsigned
gloopio_poll(void* impl)
{
GLoopIo* io = impl;
return io->ready;
}
static void
gloopio_free(void* impl)
{
GLoopIo* io = impl;
if (io->ready != 0)
glooper_delPendingIo(io->looper, io);
glooper_delIo(io->looper, io);
AFREE(io);
}
static LoopIoClass gloopio_class = {
gloopio_wantRead,
gloopio_wantWrite,
gloopio_dontWantRead,
gloopio_dontWantWrite,
gloopio_poll,
gloopio_free
};
static void
glooper_io_init(Looper* looper, LoopIo* user, int fd, LoopIoFunc callback, void* opaque)
{
GLooper* gg = (GLooper*)looper;
GLoopIo* io;
ANEW0(io);
io->fd = fd;
io->callback = callback;
io->opaque = opaque;
io->looper = (GLooper*) looper;
io->wanted = 0;
io->ready = 0;
socket_set_nonblock(fd);
glooper_addIo(gg, io);
user->impl = io;
user->clazz = (LoopIoClass*) &gloopio_class;
}
/**********************************************************************
**********************************************************************
*****
***** L O O P E R
*****
**********************************************************************
**********************************************************************/
struct GLooper {
Looper looper;
ARefSet timers[1]; /* set of all timers */
GLoopTimer* activeTimers; /* sorted list of active timers */
ARefSet ios[1]; /* set of all i/o waiters */
ARefSet pendingIos[1]; /* list of pending i/o waiters */
int numActiveIos; /* number of active LoopIo objects */
IoLooper* iolooper;
int running;
};
static void
glooper_addTimer(GLooper* looper, GLoopTimer* tt)
{
arefSet_add(looper->timers, tt);
}
static void
glooper_delTimer(GLooper* looper, GLoopTimer* tt)
{
arefSet_del(looper->timers, tt);
}
static void
glooper_addActiveTimer(GLooper* looper, GLoopTimer* tt)
{
Duration deadline = tt->deadline;
GLoopTimer** pnode = &looper->activeTimers;
for (;;) {
GLoopTimer* node = *pnode;
if (node == NULL || node->deadline > deadline)
break;
pnode = &node->activeNext;
}
tt->activeNext = *pnode;
*pnode = tt;
}
static void
glooper_delActiveTimer(GLooper* looper, GLoopTimer* tt)
{
GLoopTimer** pnode = &looper->activeTimers;
for (;;) {
if (*pnode == NULL)
break;
if (*pnode == tt) {
*pnode = tt->activeNext;
tt->activeNext = NULL;
break;
}
pnode = &(*pnode)->activeNext;
}
}
static void
glooper_addIo(GLooper* looper, GLoopIo* io)
{
arefSet_add(looper->ios, io);
}
static void
glooper_delIo(GLooper* looper, GLoopIo* io)
{
arefSet_del(looper->ios, io);
}
static void
glooper_delPendingIo(GLooper* looper, GLoopIo* io)
{
arefSet_del(looper->pendingIos, io);
}
static void
glooper_modifyFd(GLooper* looper, int fd, int oldWanted, int newWanted)
{
if (oldWanted == 0 && newWanted != 0)
looper->numActiveIos += 1;
if (oldWanted != 0 && newWanted == 0)
looper->numActiveIos -= 1;
iolooper_modify(looper->iolooper, fd, oldWanted, newWanted);
}
static Duration
glooper_now(Looper* ll)
{
return iolooper_now();
}
static void
glooper_forceQuit(Looper* ll)
{
GLooper* looper = (GLooper*)ll;
looper->running = 0;
}
static int
glooper_run(Looper* ll, Duration loop_deadline_ms)
{
GLooper* looper = (GLooper*) ll;
IoLooper* iol = looper->iolooper;
looper->running = 1;
while (looper->running)
{
int ret;
/* Exit prematurely if we detect that we don't have any active timer
* and no active LoopIo
*/
if (looper->numActiveIos == 0 && looper->activeTimers == NULL)
return EWOULDBLOCK;
/* First, compute next deadline */
Duration deadline = DURATION_INFINITE;
if (looper->activeTimers != NULL)
deadline = looper->activeTimers->deadline;
if (deadline > loop_deadline_ms)
deadline = loop_deadline_ms;
ret = iolooper_wait_absolute(iol, deadline);
if (ret < 0) { /* error, force stop ! */
break;
}
if (ret > 0) {
unsigned ready;
GLoopIo* io;
/* Add io waiters to the pending list */
AREFSET_FOREACH(looper->ios, io, {
if (io->wanted == 0)
continue;
ready = 0;
if (iolooper_is_read(iol, io->fd))
ready |= LOOP_IO_READ;
if (iolooper_is_write(iol, io->fd))
ready |= LOOP_IO_WRITE;
io->ready = ready;
if (ready != 0) {
arefSet_add(looper->pendingIos, io);
}
});
}
/* Do we have any expired timers here ? */
GLoopTimer* pendingTimers = NULL;
GLoopTimer** pendingLastP = &pendingTimers;
deadline = iolooper_now();
for (;;) {
GLoopTimer* timer = looper->activeTimers;
if (timer == NULL || timer->deadline > deadline)
break;
/* remove from active list, and append to pending one */
timer->deadline = DURATION_INFINITE;
looper->activeTimers = timer->activeNext;
*pendingLastP = timer;
timer->activeNext = NULL;
pendingLastP = &timer->activeNext;
}
/* Fire the pending timers, if any. We do that in a separate
* step because the callbacks could modify the active list
* by starting/stopping other timers.
*/
{
GLoopTimer* timer;
while ((timer = pendingTimers) != NULL) {
pendingTimers = timer->activeNext;
timer->activeNext = NULL;
timer->callback(timer->opaque);
}
}
/* Now fire the pending ios */
{
GLoopIo* io;
AREFSET_FOREACH(looper->pendingIos,io,{
io->callback(io->opaque,io->fd,io->ready);
});
arefSet_clear(looper->pendingIos);
}
if (deadline > loop_deadline_ms)
return ETIMEDOUT;
}
return 0;
}
static void
glooper_free(Looper* ll)
{
GLooper* looper = (GLooper*)ll;
arefSet_done(looper->timers);
looper->activeTimers = NULL;
arefSet_done(looper->ios);
arefSet_done(looper->pendingIos);
iolooper_free(looper->iolooper);
looper->iolooper = NULL;
AFREE(looper);
}
Looper* looper_newGeneric(void)
{
GLooper* looper;
ANEW0(looper);
looper->iolooper = iolooper_new();
looper->looper.now = glooper_now;
looper->looper.timer_init = glooper_timer_init;
looper->looper.io_init = glooper_io_init;
looper->looper.run = glooper_run;
looper->looper.forceQuit = glooper_forceQuit;
looper->looper.destroy = glooper_free;
/* Our implementation depends on these values being equal */
AASSERT_INT(LOOP_IO_READ, IOLOOPER_READ);
AASSERT_INT(LOOP_IO_WRITE, IOLOOPER_WRITE);
return &looper->looper;
}