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