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
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