From 7b76d9ff7e4478692a6137a21224869f651a8a8d Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sun, 30 Nov 2014 18:41:11 +0100 Subject: [PATCH] add timers to mainloop they are called "wakeups", though, as timer_t already exists in time.h. --- src/Makefile.am | 4 ++ src/common.h | 17 ++++++- src/tst_timers.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++ src/util.c | 112 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 src/tst_timers.c diff --git a/src/Makefile.am b/src/Makefile.am index 85d5bab..8ae3b53 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,6 +12,10 @@ noinst_HEADERS = common.h config.h driver.h sync.h socket.h mdconvert_SOURCES = mdconvert.c mdconvert_LDADD = -ldb +EXTRA_PROGRAMS = tst_timers + +tst_timers_SOURCES = tst_timers.c util.c + man_MANS = mbsync.1 mdconvert.1 exampledir = $(docdir)/examples diff --git a/src/common.h b/src/common.h index eb021b8..d10a791 100644 --- a/src/common.h +++ b/src/common.h @@ -28,6 +28,7 @@ #include #include #include +#include typedef unsigned char uchar; typedef unsigned short ushort; @@ -103,7 +104,6 @@ int starts_with( const char *str, int strl, const char *cmp, int cmpl ); int equals( const char *str, int strl, const char *cmp, int cmpl ); #ifndef HAVE_TIMEGM -# include time_t timegm( struct tm *tm ); #endif @@ -127,6 +127,10 @@ uchar arc4_getbyte( void ); int bucketsForSize( int size ); +typedef struct list_head { + struct list_head *next, *prev; +} list_head_t; + typedef struct notifier { struct notifier *next; void (*cb)( int what, void *aux ); @@ -152,6 +156,17 @@ void conf_notifier( notifier_t *sn, int and_events, int or_events ); static INLINE void fake_notifier( notifier_t *sn, int events ) { sn->faked |= events; } void wipe_notifier( notifier_t *sn ); +typedef struct { + list_head_t links; + void (*cb)( void *aux ); + void *aux; + time_t timeout; +} wakeup_t; + +void init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux ); +void conf_wakeup( wakeup_t *tmr, int timeout ); +void wipe_wakeup( wakeup_t *tmr ); + void main_loop( void ); #endif diff --git a/src/tst_timers.c b/src/tst_timers.c new file mode 100644 index 0000000..18f1e00 --- /dev/null +++ b/src/tst_timers.c @@ -0,0 +1,116 @@ +/* + * mbsync - mailbox synchronizer + * Copyright (C) 2014 Oswald Buddenhagen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * As a special exception, mbsync may be linked with the OpenSSL library, + * despite that library's more restrictive license. + */ + +#include "common.h" + +#include +#include +#include + +/* Just to satisfy the references in util.c */ +int DFlags; +const char *Home; + +struct tst { + int id; + int first, other, morph_at, morph_to; + time_t start; + wakeup_t timer; + wakeup_t morph_timer; +}; + +static void +timer_start( struct tst *timer, int to ) +{ + printf( "starting timer %d, should expire after %d\n", timer->id, to ); + time( &timer->start ); + conf_wakeup( &timer->timer, to ); +} + +static void +timed_out( void *aux ) +{ + struct tst *timer = (struct tst *)aux; + + printf( "timer %d expired after %d, repeat %d\n", + timer->id, (int)(time( 0 ) - timer->start), timer->other ); + if (timer->other >= 0) { + timer_start( timer, timer->other ); + } else { + wipe_wakeup( &timer->timer ); + wipe_wakeup( &timer->morph_timer ); + free( timer ); + } +} + +static void +morph_timed_out( void *aux ) +{ + struct tst *timer = (struct tst *)aux; + + printf( "morphing timer %d after %d\n", + timer->id, (int)(time( 0 ) - timer->start) ); + timer_start( timer, timer->morph_to ); +} + +static int nextid; + +int +main( int argc, char **argv ) +{ + int i; + + for (i = 1; i < argc; i++) { + char *val = argv[i]; + struct tst *timer = nfmalloc( sizeof(*timer) ); + init_wakeup( &timer->timer, timed_out, timer ); + init_wakeup( &timer->morph_timer, morph_timed_out, timer ); + timer->id = ++nextid; + timer->first = strtol( val, &val, 0 ); + if (*val == '@') { + timer->other = timer->first; + timer->first = strtol( ++val, &val, 0 ); + } else { + timer->other = -1; + } + if (*val == ':') { + timer->morph_to = strtol( ++val, &val, 0 ); + if (*val != '@') + goto fail; + timer->morph_at = strtol( ++val, &val, 0 ); + } else { + timer->morph_at = -1; + } + if (*val) { + fail: + fprintf( stderr, "Fatal: syntax error in %s, use [@][:@]\n", argv[i] ); + return 1; + } + timer_start( timer, timer->first ); + if (timer->morph_at >= 0) { + printf( "timer %d, should morph after %d\n", timer->id, timer->morph_at ); + conf_wakeup( &timer->morph_timer, timer->morph_at ); + } + } + + main_loop(); + return 0; +} diff --git a/src/util.c b/src/util.c index b11b137..d3448c5 100644 --- a/src/util.c +++ b/src/util.c @@ -584,6 +584,29 @@ bucketsForSize( int size ) } } +static void +list_prepend( list_head_t *head, list_head_t *to ) +{ + assert( !head->next ); + assert( to->next ); + assert( to->prev->next == to ); + head->next = to; + head->prev = to->prev; + head->prev->next = head; + to->prev = head; +} + +static void +list_unlink( list_head_t *head ) +{ + assert( head->next ); + assert( head->next->prev == head); + assert( head->prev->next == head); + head->next->prev = head->prev; + head->prev->next = head->next; + head->next = head->prev = 0; +} + static notifier_t *notifiers; static int changed; /* Iterator may be invalid now. */ #ifdef HAVE_SYS_POLL_H @@ -653,6 +676,67 @@ wipe_notifier( notifier_t *sn ) #endif } +static int nowvalid; +static time_t now; + +static time_t +get_now( void ) +{ + if (!nowvalid) { + nowvalid = 1; + return time( &now ); + } + return now; +} + +static list_head_t timers = { &timers, &timers }; + +void +init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux ) +{ + tmr->cb = cb; + tmr->aux = aux; + tmr->links.next = tmr->links.prev = 0; +} + +void +wipe_wakeup( wakeup_t *tmr ) +{ + if (tmr->links.next) + list_unlink( &tmr->links ); +} + +void +conf_wakeup( wakeup_t *tmr, int to ) +{ + list_head_t *head, *succ; + + if (to < 0) { + if (tmr->links.next) + list_unlink( &tmr->links ); + } else { + time_t timeout = get_now() + to; + tmr->timeout = timeout; + if (!to) { + /* We always prepend null timers, to cluster related events. */ + succ = timers.next; + } else { + /* We start at the end in the expectation that the newest timer is likely to fire last + * (which will be true only if all timeouts are equal, but it's an as good guess as any). */ + for (succ = &timers; (head = succ->prev) != &timers; succ = head) { + if (head != &tmr->links && timeout > ((wakeup_t *)head)->timeout) + break; + } + assert( head != &tmr->links ); + } + if (succ != &tmr->links) { + if (tmr->links.next) + list_unlink( &tmr->links ); + list_prepend( &tmr->links, succ ); + } + } +} + #define shifted_bit(in, from, to) \ (((uint)(in) & from) \ / (from > to ? from / to : 1) \ @@ -661,11 +745,23 @@ wipe_notifier( notifier_t *sn ) static void event_wait( void ) { + list_head_t *head; notifier_t *sn; int m; #ifdef HAVE_SYS_POLL_H int timeout = -1; + nowvalid = 0; + if ((head = timers.next) != &timers) { + wakeup_t *tmr = (wakeup_t *)head; + int delta = tmr->timeout - get_now(); + if (delta <= 0) { + list_unlink( head ); + tmr->cb( tmr->aux ); + return; + } + timeout = delta * 1000; + } for (sn = notifiers; sn; sn = sn->next) if (sn->faked) { timeout = 0; @@ -689,10 +785,24 @@ event_wait( void ) } #else struct timeval *timeout = 0; + struct timeval to_tv; static struct timeval null_tv; fd_set rfds, wfds, efds; int fd; + nowvalid = 0; + if ((head = timers.next) != &timers) { + wakeup_t *tmr = (wakeup_t *)head; + int delta = tmr->timeout - get_now(); + if (delta <= 0) { + list_unlink( head ); + tmr->cb( tmr->aux ); + return; + } + to_tv.tv_sec = delta; + to_tv.tv_usec = 0; + timeout = &to_tv; + } FD_ZERO( &rfds ); FD_ZERO( &wfds ); FD_ZERO( &efds ); @@ -737,6 +847,6 @@ event_wait( void ) void main_loop( void ) { - while (notifiers) + while (notifiers || timers.next != &timers) event_wait(); }