Home | History | Annotate | Download | only in avahi-core
      1 /***
      2   This file is part of avahi.
      3 
      4   avahi is free software; you can redistribute it and/or modify it
      5   under the terms of the GNU Lesser General Public License as
      6   published by the Free Software Foundation; either version 2.1 of the
      7   License, or (at your option) any later version.
      8 
      9   avahi is distributed in the hope that it will be useful, but WITHOUT
     10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
     12   Public License for more details.
     13 
     14   You should have received a copy of the GNU Lesser General Public
     15   License along with avahi; if not, write to the Free Software
     16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
     17   USA.
     18 ***/
     19 
     20 #ifdef HAVE_CONFIG_H
     21 #include <config.h>
     22 #endif
     23 
     24 #include <assert.h>
     25 #include <stdlib.h>
     26 
     27 #include <avahi-common/timeval.h>
     28 #include "avahi-common/avahi-malloc.h"
     29 
     30 #include "timeeventq.h"
     31 #include "log.h"
     32 
     33 struct AvahiTimeEvent {
     34     AvahiTimeEventQueue *queue;
     35     AvahiPrioQueueNode *node;
     36     struct timeval expiry;
     37     struct timeval last_run;
     38     AvahiTimeEventCallback callback;
     39     void* userdata;
     40 };
     41 
     42 struct AvahiTimeEventQueue {
     43     const AvahiPoll *poll_api;
     44     AvahiPrioQueue *prioq;
     45     AvahiTimeout *timeout;
     46 };
     47 
     48 static int compare(const void* _a, const void* _b) {
     49     const AvahiTimeEvent *a = _a,  *b = _b;
     50     int ret;
     51 
     52     if ((ret = avahi_timeval_compare(&a->expiry, &b->expiry)) != 0)
     53         return ret;
     54 
     55     /* If both exevents are scheduled for the same time, put the entry
     56      * that has been run earlier the last time first. */
     57     return avahi_timeval_compare(&a->last_run, &b->last_run);
     58 }
     59 
     60 static AvahiTimeEvent* time_event_queue_root(AvahiTimeEventQueue *q) {
     61     assert(q);
     62 
     63     return q->prioq->root ? q->prioq->root->data : NULL;
     64 }
     65 
     66 static void update_timeout(AvahiTimeEventQueue *q) {
     67     AvahiTimeEvent *e;
     68     assert(q);
     69 
     70     if ((e = time_event_queue_root(q)))
     71         q->poll_api->timeout_update(q->timeout, &e->expiry);
     72     else
     73         q->poll_api->timeout_update(q->timeout, NULL);
     74 }
     75 
     76 static void expiration_event(AVAHI_GCC_UNUSED AvahiTimeout *timeout, void *userdata) {
     77     AvahiTimeEventQueue *q = userdata;
     78     AvahiTimeEvent *e;
     79 
     80     if ((e = time_event_queue_root(q))) {
     81         struct timeval now;
     82 
     83         gettimeofday(&now, NULL);
     84 
     85         /* Check if expired */
     86         if (avahi_timeval_compare(&now, &e->expiry) >= 0) {
     87 
     88             /* Make sure to move the entry away from the front */
     89             e->last_run = now;
     90             avahi_prio_queue_shuffle(q->prioq, e->node);
     91 
     92             /* Run it */
     93             assert(e->callback);
     94             e->callback(e, e->userdata);
     95 
     96             update_timeout(q);
     97             return;
     98         }
     99     }
    100 
    101     avahi_log_debug(__FILE__": Strange, expiration_event() called, but nothing really happened.");
    102     update_timeout(q);
    103 }
    104 
    105 static void fix_expiry_time(AvahiTimeEvent *e) {
    106     struct timeval now;
    107     assert(e);
    108 
    109     return; /*** DO WE REALLY NEED THIS? ***/
    110 
    111     gettimeofday(&now, NULL);
    112 
    113     if (avahi_timeval_compare(&now, &e->expiry) > 0)
    114         e->expiry = now;
    115 }
    116 
    117 AvahiTimeEventQueue* avahi_time_event_queue_new(const AvahiPoll *poll_api) {
    118     AvahiTimeEventQueue *q;
    119 
    120     if (!(q = avahi_new(AvahiTimeEventQueue, 1))) {
    121         avahi_log_error(__FILE__": Out of memory");
    122         goto oom;
    123     }
    124 
    125     q->poll_api = poll_api;
    126 
    127     if (!(q->prioq = avahi_prio_queue_new(compare)))
    128         goto oom;
    129 
    130     if (!(q->timeout = poll_api->timeout_new(poll_api, NULL, expiration_event, q)))
    131         goto oom;
    132 
    133     return q;
    134 
    135 oom:
    136 
    137     if (q) {
    138         avahi_free(q);
    139 
    140         if (q->prioq)
    141             avahi_prio_queue_free(q->prioq);
    142     }
    143 
    144     return NULL;
    145 }
    146 
    147 void avahi_time_event_queue_free(AvahiTimeEventQueue *q) {
    148     AvahiTimeEvent *e;
    149 
    150     assert(q);
    151 
    152     while ((e = time_event_queue_root(q)))
    153         avahi_time_event_free(e);
    154     avahi_prio_queue_free(q->prioq);
    155 
    156     q->poll_api->timeout_free(q->timeout);
    157 
    158     avahi_free(q);
    159 }
    160 
    161 AvahiTimeEvent* avahi_time_event_new(
    162     AvahiTimeEventQueue *q,
    163     const struct timeval *timeval,
    164     AvahiTimeEventCallback callback,
    165     void* userdata) {
    166 
    167     AvahiTimeEvent *e;
    168 
    169     assert(q);
    170     assert(callback);
    171     assert(userdata);
    172 
    173     if (!(e = avahi_new(AvahiTimeEvent, 1))) {
    174         avahi_log_error(__FILE__": Out of memory");
    175         return NULL; /* OOM */
    176     }
    177 
    178     e->queue = q;
    179     e->callback = callback;
    180     e->userdata = userdata;
    181 
    182     if (timeval)
    183         e->expiry = *timeval;
    184     else {
    185         e->expiry.tv_sec = 0;
    186         e->expiry.tv_usec = 0;
    187     }
    188 
    189     fix_expiry_time(e);
    190 
    191     e->last_run.tv_sec = 0;
    192     e->last_run.tv_usec = 0;
    193 
    194     if (!(e->node = avahi_prio_queue_put(q->prioq, e))) {
    195         avahi_free(e);
    196         return NULL;
    197     }
    198 
    199     update_timeout(q);
    200     return e;
    201 }
    202 
    203 void avahi_time_event_free(AvahiTimeEvent *e) {
    204     AvahiTimeEventQueue *q;
    205     assert(e);
    206 
    207     q = e->queue;
    208 
    209     avahi_prio_queue_remove(q->prioq, e->node);
    210     avahi_free(e);
    211 
    212     update_timeout(q);
    213 }
    214 
    215 void avahi_time_event_update(AvahiTimeEvent *e, const struct timeval *timeval) {
    216     assert(e);
    217     assert(timeval);
    218 
    219     e->expiry = *timeval;
    220     fix_expiry_time(e);
    221     avahi_prio_queue_shuffle(e->queue->prioq, e->node);
    222 
    223     update_timeout(e->queue);
    224 }
    225 
    226