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