michael@0: /* $NetBSD: ev_timers.c,v 1.2 2004/05/20 19:52:31 christos Exp $ */ michael@0: michael@0: /* michael@0: * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") michael@0: * Copyright (c) 1995-1999 by Internet Software Consortium michael@0: * michael@0: * Permission to use, copy, modify, and distribute this software for any michael@0: * purpose with or without fee is hereby granted, provided that the above michael@0: * copyright notice and this permission notice appear in all copies. michael@0: * michael@0: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES michael@0: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF michael@0: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR michael@0: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES michael@0: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN michael@0: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT michael@0: * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. michael@0: */ michael@0: michael@0: /* ev_timers.c - implement timers for the eventlib michael@0: * vix 09sep95 [initial] michael@0: */ michael@0: michael@0: /* michael@0: * This version of this file is derived from Android 2.3 "Gingerbread", michael@0: * which contains uncredited changes by Android/Google developers. It has michael@0: * been modified in 2011 for use in the Android build of Mozilla Firefox by michael@0: * Mozilla contributors (including Michael Edwards , michael@0: * and Steve Workman ). michael@0: * These changes are offered under the same license as the original NetBSD michael@0: * file, whose copyright and license are unchanged above. michael@0: */ michael@0: michael@0: #define ANDROID_CHANGES 1 michael@0: #define MOZILLA_NECKO_EXCLUDE_CODE 1 michael@0: michael@0: #include michael@0: #if !defined(LINT) && !defined(CODECENTER) && !defined(lint) michael@0: #ifdef notdef michael@0: static const char rcsid[] = "Id: ev_timers.c,v 1.2.2.1.4.5 2004/03/17 02:39:13 marka Exp"; michael@0: #else michael@0: __RCSID("$NetBSD: ev_timers.c,v 1.2 2004/05/20 19:52:31 christos Exp $"); michael@0: #endif michael@0: #endif michael@0: michael@0: /* Import. */ michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "assertions.h" michael@0: #include "eventlib.h" michael@0: #include "eventlib_p.h" michael@0: michael@0: /* Constants. */ michael@0: michael@0: #define MILLION 1000000 michael@0: #define BILLION 1000000000 michael@0: michael@0: /* Forward. */ michael@0: michael@0: #ifndef _LIBC michael@0: static int due_sooner(void *, void *); michael@0: static void set_index(void *, int); michael@0: static void free_timer(void *, void *); michael@0: static void print_timer(void *, void *); michael@0: static void idle_timeout(evContext, void *, struct timespec, struct timespec); michael@0: michael@0: /* Private type. */ michael@0: michael@0: typedef struct { michael@0: evTimerFunc func; michael@0: void * uap; michael@0: struct timespec lastTouched; michael@0: struct timespec max_idle; michael@0: evTimer * timer; michael@0: } idle_timer; michael@0: #endif michael@0: michael@0: /* Public. */ michael@0: michael@0: struct timespec michael@0: evConsTime(time_t sec, long nsec) { michael@0: struct timespec x; michael@0: michael@0: x.tv_sec = sec; michael@0: x.tv_nsec = nsec; michael@0: return (x); michael@0: } michael@0: michael@0: struct timespec michael@0: evAddTime(struct timespec addend1, struct timespec addend2) { michael@0: struct timespec x; michael@0: michael@0: x.tv_sec = addend1.tv_sec + addend2.tv_sec; michael@0: x.tv_nsec = addend1.tv_nsec + addend2.tv_nsec; michael@0: if (x.tv_nsec >= BILLION) { michael@0: x.tv_sec++; michael@0: x.tv_nsec -= BILLION; michael@0: } michael@0: return (x); michael@0: } michael@0: michael@0: struct timespec michael@0: evSubTime(struct timespec minuend, struct timespec subtrahend) { michael@0: struct timespec x; michael@0: michael@0: x.tv_sec = minuend.tv_sec - subtrahend.tv_sec; michael@0: if (minuend.tv_nsec >= subtrahend.tv_nsec) michael@0: x.tv_nsec = minuend.tv_nsec - subtrahend.tv_nsec; michael@0: else { michael@0: x.tv_nsec = BILLION - subtrahend.tv_nsec + minuend.tv_nsec; michael@0: x.tv_sec--; michael@0: } michael@0: return (x); michael@0: } michael@0: michael@0: int michael@0: evCmpTime(struct timespec a, struct timespec b) { michael@0: long x = a.tv_sec - b.tv_sec; michael@0: michael@0: if (x == 0L) michael@0: x = a.tv_nsec - b.tv_nsec; michael@0: return (x < 0L ? (-1) : x > 0L ? (1) : (0)); michael@0: } michael@0: michael@0: struct timespec michael@0: evNowTime() { michael@0: struct timeval now; michael@0: #ifdef CLOCK_REALTIME michael@0: struct timespec tsnow; michael@0: int m = CLOCK_REALTIME; michael@0: michael@0: #ifdef CLOCK_MONOTONIC michael@0: if (__evOptMonoTime) michael@0: m = CLOCK_MONOTONIC; michael@0: #endif michael@0: if (clock_gettime(m, &tsnow) == 0) michael@0: return (tsnow); michael@0: #endif michael@0: if (gettimeofday(&now, NULL) < 0) michael@0: return (evConsTime(0L, 0L)); michael@0: return (evTimeSpec(now)); michael@0: } michael@0: michael@0: #ifndef MOZILLA_NECKO_EXCLUDE_CODE michael@0: struct timespec michael@0: evUTCTime(void) { michael@0: struct timeval now; michael@0: #ifdef CLOCK_REALTIME michael@0: struct timespec tsnow; michael@0: if (clock_gettime(CLOCK_REALTIME, &tsnow) == 0) michael@0: return (tsnow); michael@0: #endif michael@0: if (gettimeofday(&now, NULL) < 0) michael@0: return (evConsTime(0L, 0L)); michael@0: return (evTimeSpec(now)); michael@0: } michael@0: michael@0: #ifndef _LIBC michael@0: struct timespec michael@0: evLastEventTime(evContext opaqueCtx) { michael@0: evContext_p *ctx = opaqueCtx.opaque; michael@0: michael@0: return (ctx->lastEventTime); michael@0: } michael@0: #endif michael@0: #endif michael@0: michael@0: struct timespec michael@0: evTimeSpec(struct timeval tv) { michael@0: struct timespec ts; michael@0: michael@0: ts.tv_sec = tv.tv_sec; michael@0: ts.tv_nsec = tv.tv_usec * 1000; michael@0: return (ts); michael@0: } michael@0: michael@0: #ifndef MOZILLA_NECKO_EXCLUDE_CODE michael@0: struct timeval michael@0: evTimeVal(struct timespec ts) { michael@0: struct timeval tv; michael@0: michael@0: tv.tv_sec = ts.tv_sec; michael@0: tv.tv_usec = ts.tv_nsec / 1000; michael@0: return (tv); michael@0: } michael@0: michael@0: #ifndef _LIBC michael@0: int michael@0: evSetTimer(evContext opaqueCtx, michael@0: evTimerFunc func, michael@0: void *uap, michael@0: struct timespec due, michael@0: struct timespec inter, michael@0: evTimerID *opaqueID michael@0: ) { michael@0: evContext_p *ctx = opaqueCtx.opaque; michael@0: evTimer *id; michael@0: michael@0: printf("evSetTimer(ctx %p, func %p, uap %p, due %ld.%09ld, inter %ld.%09ld)\n", michael@0: ctx, func, uap, michael@0: (long)due.tv_sec, due.tv_nsec, michael@0: (long)inter.tv_sec, inter.tv_nsec); michael@0: michael@0: #ifdef __hpux michael@0: /* michael@0: * tv_sec and tv_nsec are unsigned. michael@0: */ michael@0: if (due.tv_nsec >= BILLION) michael@0: EV_ERR(EINVAL); michael@0: michael@0: if (inter.tv_nsec >= BILLION) michael@0: EV_ERR(EINVAL); michael@0: #else michael@0: if (due.tv_sec < 0 || due.tv_nsec < 0 || due.tv_nsec >= BILLION) michael@0: EV_ERR(EINVAL); michael@0: michael@0: if (inter.tv_sec < 0 || inter.tv_nsec < 0 || inter.tv_nsec >= BILLION) michael@0: EV_ERR(EINVAL); michael@0: #endif michael@0: michael@0: /* due={0,0} is a magic cookie meaning "now." */ michael@0: if (due.tv_sec == (time_t)0 && due.tv_nsec == 0L) michael@0: due = evNowTime(); michael@0: michael@0: /* Allocate and fill. */ michael@0: OKNEW(id); michael@0: id->func = func; michael@0: id->uap = uap; michael@0: id->due = due; michael@0: id->inter = inter; michael@0: michael@0: if (heap_insert(ctx->timers, id) < 0) michael@0: return (-1); michael@0: michael@0: /* Remember the ID if the caller provided us a place for it. */ michael@0: if (opaqueID) michael@0: opaqueID->opaque = id; michael@0: michael@0: if (ctx->debug > 7) { michael@0: printf("timers after evSetTimer:\n"); michael@0: (void) heap_for_each(ctx->timers, print_timer, (void *)ctx); michael@0: } michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: int michael@0: evClearTimer(evContext opaqueCtx, evTimerID id) { michael@0: evContext_p *ctx = opaqueCtx.opaque; michael@0: evTimer *del = id.opaque; michael@0: michael@0: if (ctx->cur != NULL && michael@0: ctx->cur->type == Timer && michael@0: ctx->cur->u.timer.this == del) { michael@0: printf("deferring delete of timer (executing)\n"); michael@0: /* michael@0: * Setting the interval to zero ensures that evDrop() will michael@0: * clean up the timer. michael@0: */ michael@0: del->inter = evConsTime(0, 0); michael@0: return (0); michael@0: } michael@0: michael@0: if (heap_element(ctx->timers, del->index) != del) michael@0: EV_ERR(ENOENT); michael@0: michael@0: if (heap_delete(ctx->timers, del->index) < 0) michael@0: return (-1); michael@0: FREE(del); michael@0: michael@0: if (ctx->debug > 7) { michael@0: printf("timers after evClearTimer:\n"); michael@0: (void) heap_for_each(ctx->timers, print_timer, (void *)ctx); michael@0: } michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: int michael@0: evConfigTimer(evContext opaqueCtx, michael@0: evTimerID id, michael@0: const char *param, michael@0: int value michael@0: ) { michael@0: evContext_p *ctx = opaqueCtx.opaque; michael@0: evTimer *timer = id.opaque; michael@0: int result=0; michael@0: michael@0: UNUSED(value); michael@0: michael@0: if (heap_element(ctx->timers, timer->index) != timer) michael@0: EV_ERR(ENOENT); michael@0: michael@0: if (strcmp(param, "rate") == 0) michael@0: timer->mode |= EV_TMR_RATE; michael@0: else if (strcmp(param, "interval") == 0) michael@0: timer->mode &= ~EV_TMR_RATE; michael@0: else michael@0: EV_ERR(EINVAL); michael@0: michael@0: return (result); michael@0: } michael@0: michael@0: int michael@0: evResetTimer(evContext opaqueCtx, michael@0: evTimerID id, michael@0: evTimerFunc func, michael@0: void *uap, michael@0: struct timespec due, michael@0: struct timespec inter michael@0: ) { michael@0: evContext_p *ctx = opaqueCtx.opaque; michael@0: evTimer *timer = id.opaque; michael@0: struct timespec old_due; michael@0: int result=0; michael@0: michael@0: if (heap_element(ctx->timers, timer->index) != timer) michael@0: EV_ERR(ENOENT); michael@0: michael@0: #ifdef __hpux michael@0: /* michael@0: * tv_sec and tv_nsec are unsigned. michael@0: */ michael@0: if (due.tv_nsec >= BILLION) michael@0: EV_ERR(EINVAL); michael@0: michael@0: if (inter.tv_nsec >= BILLION) michael@0: EV_ERR(EINVAL); michael@0: #else michael@0: if (due.tv_sec < 0 || due.tv_nsec < 0 || due.tv_nsec >= BILLION) michael@0: EV_ERR(EINVAL); michael@0: michael@0: if (inter.tv_sec < 0 || inter.tv_nsec < 0 || inter.tv_nsec >= BILLION) michael@0: EV_ERR(EINVAL); michael@0: #endif michael@0: michael@0: old_due = timer->due; michael@0: michael@0: timer->func = func; michael@0: timer->uap = uap; michael@0: timer->due = due; michael@0: timer->inter = inter; michael@0: michael@0: switch (evCmpTime(due, old_due)) { michael@0: case -1: michael@0: result = heap_increased(ctx->timers, timer->index); michael@0: break; michael@0: case 0: michael@0: result = 0; michael@0: break; michael@0: case 1: michael@0: result = heap_decreased(ctx->timers, timer->index); michael@0: break; michael@0: } michael@0: michael@0: if (ctx->debug > 7) { michael@0: printf("timers after evResetTimer:\n"); michael@0: (void) heap_for_each(ctx->timers, print_timer, (void *)ctx); michael@0: } michael@0: michael@0: return (result); michael@0: } michael@0: michael@0: int michael@0: evSetIdleTimer(evContext opaqueCtx, michael@0: evTimerFunc func, michael@0: void *uap, michael@0: struct timespec max_idle, michael@0: evTimerID *opaqueID michael@0: ) { michael@0: evContext_p *ctx = opaqueCtx.opaque; michael@0: idle_timer *tt; michael@0: michael@0: /* Allocate and fill. */ michael@0: OKNEW(tt); michael@0: tt->func = func; michael@0: tt->uap = uap; michael@0: tt->lastTouched = ctx->lastEventTime; michael@0: tt->max_idle = max_idle; michael@0: michael@0: if (evSetTimer(opaqueCtx, idle_timeout, tt, michael@0: evAddTime(ctx->lastEventTime, max_idle), michael@0: max_idle, opaqueID) < 0) { michael@0: FREE(tt); michael@0: return (-1); michael@0: } michael@0: michael@0: tt->timer = opaqueID->opaque; michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: int michael@0: evClearIdleTimer(evContext opaqueCtx, evTimerID id) { michael@0: evTimer *del = id.opaque; michael@0: idle_timer *tt = del->uap; michael@0: michael@0: FREE(tt); michael@0: return (evClearTimer(opaqueCtx, id)); michael@0: } michael@0: michael@0: int michael@0: evResetIdleTimer(evContext opaqueCtx, michael@0: evTimerID opaqueID, michael@0: evTimerFunc func, michael@0: void *uap, michael@0: struct timespec max_idle michael@0: ) { michael@0: evContext_p *ctx = opaqueCtx.opaque; michael@0: evTimer *timer = opaqueID.opaque; michael@0: idle_timer *tt = timer->uap; michael@0: michael@0: tt->func = func; michael@0: tt->uap = uap; michael@0: tt->lastTouched = ctx->lastEventTime; michael@0: tt->max_idle = max_idle; michael@0: michael@0: return (evResetTimer(opaqueCtx, opaqueID, idle_timeout, tt, michael@0: evAddTime(ctx->lastEventTime, max_idle), michael@0: max_idle)); michael@0: } michael@0: michael@0: int michael@0: evTouchIdleTimer(evContext opaqueCtx, evTimerID id) { michael@0: evContext_p *ctx = opaqueCtx.opaque; michael@0: evTimer *t = id.opaque; michael@0: idle_timer *tt = t->uap; michael@0: michael@0: tt->lastTouched = ctx->lastEventTime; michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: /* Public to the rest of eventlib. */ michael@0: michael@0: heap_context michael@0: evCreateTimers(const evContext_p *ctx) { michael@0: michael@0: UNUSED(ctx); michael@0: michael@0: return (heap_new(due_sooner, set_index, 2048)); michael@0: } michael@0: michael@0: void michael@0: evDestroyTimers(const evContext_p *ctx) { michael@0: (void) heap_for_each(ctx->timers, free_timer, NULL); michael@0: (void) heap_free(ctx->timers); michael@0: } michael@0: michael@0: /* Private. */ michael@0: michael@0: static int michael@0: due_sooner(void *a, void *b) { michael@0: evTimer *a_timer, *b_timer; michael@0: michael@0: a_timer = a; michael@0: b_timer = b; michael@0: return (evCmpTime(a_timer->due, b_timer->due) < 0); michael@0: } michael@0: michael@0: static void michael@0: set_index(void *what, int idx) { michael@0: evTimer *timer; michael@0: michael@0: timer = what; michael@0: timer->index = idx; michael@0: } michael@0: michael@0: static void michael@0: free_timer(void *what, void *uap) { michael@0: evTimer *t = what; michael@0: michael@0: UNUSED(uap); michael@0: michael@0: FREE(t); michael@0: } michael@0: michael@0: static void michael@0: print_timer(void *what, void *uap) { michael@0: evTimer *cur = what; michael@0: evContext_p *ctx = uap; michael@0: michael@0: cur = what; michael@0: evPrintf(ctx, 7, michael@0: " func %p, uap %p, due %ld.%09ld, inter %ld.%09ld\n", michael@0: cur->func, cur->uap, michael@0: (long)cur->due.tv_sec, cur->due.tv_nsec, michael@0: (long)cur->inter.tv_sec, cur->inter.tv_nsec); michael@0: } michael@0: michael@0: static void michael@0: idle_timeout(evContext opaqueCtx, michael@0: void *uap, michael@0: struct timespec due, michael@0: struct timespec inter michael@0: ) { michael@0: evContext_p *ctx = opaqueCtx.opaque; michael@0: idle_timer *this = uap; michael@0: struct timespec idle; michael@0: michael@0: UNUSED(due); michael@0: UNUSED(inter); michael@0: michael@0: idle = evSubTime(ctx->lastEventTime, this->lastTouched); michael@0: if (evCmpTime(idle, this->max_idle) >= 0) { michael@0: (this->func)(opaqueCtx, this->uap, this->timer->due, michael@0: this->max_idle); michael@0: /* michael@0: * Setting the interval to zero will cause the timer to michael@0: * be cleaned up in evDrop(). michael@0: */ michael@0: this->timer->inter = evConsTime(0L, 0L); michael@0: FREE(this); michael@0: } else { michael@0: /* evDrop() will reschedule the timer. */ michael@0: this->timer->inter = evSubTime(this->max_idle, idle); michael@0: } michael@0: } michael@0: #endif michael@0: #endif