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
33struct AvahiTimeEvent {
34    AvahiTimeEventQueue *queue;
35    AvahiPrioQueueNode *node;
36    struct timeval expiry;
37    struct timeval last_run;
38    AvahiTimeEventCallback callback;
39    void* userdata;
40};
41
42struct AvahiTimeEventQueue {
43    const AvahiPoll *poll_api;
44    AvahiPrioQueue *prioq;
45    AvahiTimeout *timeout;
46};
47
48static 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
60static AvahiTimeEvent* time_event_queue_root(AvahiTimeEventQueue *q) {
61    assert(q);
62
63    return q->prioq->root ? q->prioq->root->data : NULL;
64}
65
66static 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
76static 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
105static 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
117AvahiTimeEventQueue* 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
135oom:
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
147void 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
161AvahiTimeEvent* 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
203void 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
215void 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