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/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 
compare(const void * _a,const void * _b)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 
time_event_queue_root(AvahiTimeEventQueue * q)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 
update_timeout(AvahiTimeEventQueue * q)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 
expiration_event(AVAHI_GCC_UNUSED AvahiTimeout * timeout,void * userdata)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 
fix_expiry_time(AvahiTimeEvent * e)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 
avahi_time_event_queue_new(const AvahiPoll * poll_api)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 
avahi_time_event_queue_free(AvahiTimeEventQueue * q)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 
avahi_time_event_new(AvahiTimeEventQueue * q,const struct timeval * timeval,AvahiTimeEventCallback callback,void * userdata)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 
avahi_time_event_free(AvahiTimeEvent * e)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 
avahi_time_event_update(AvahiTimeEvent * e,const struct timeval * timeval)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