cvs add...
[platform/upstream/dbus.git] / bus / expirelist.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* expirelist.c  List of items that expire
3  *
4  * Copyright (C) 2003  Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "expirelist.h"
25 #include "test.h"
26 #include <dbus/dbus-internals.h>
27 #include <dbus/dbus-mainloop.h>
28 #include <dbus/dbus-timeout.h>
29
30 static dbus_bool_t expire_timeout_handler (void *data);
31
32 static void
33 call_timeout_callback (DBusTimeout   *timeout,
34                        void          *data)
35 {
36   /* can return FALSE on OOM but we just let it fire again later */
37   dbus_timeout_handle (timeout);
38 }
39
40 BusExpireList*
41 bus_expire_list_new (DBusLoop      *loop,
42                      int            expire_after,
43                      BusExpireFunc  expire_func,
44                      void          *data)
45 {
46   BusExpireList *list;
47
48   list = dbus_new0 (BusExpireList, 1);
49   if (list == NULL)
50     return NULL;
51
52   list->expire_func = expire_func;
53   list->data = data;
54   list->loop = loop;
55   list->expire_after = expire_after;
56
57   list->timeout = _dbus_timeout_new (100, /* irrelevant */
58                                      expire_timeout_handler,
59                                      list, NULL);
60   if (list->timeout == NULL)
61     goto failed;
62
63   _dbus_timeout_set_enabled (list->timeout, FALSE);
64
65   if (!_dbus_loop_add_timeout (list->loop,
66                                list->timeout,
67                                call_timeout_callback, NULL, NULL))
68     goto failed;
69
70   return list;
71
72  failed:
73   if (list->timeout)
74     _dbus_timeout_unref (list->timeout);
75
76   dbus_free (list);
77
78   return NULL;
79 }
80
81 void
82 bus_expire_list_free (BusExpireList *list)
83 {
84   _dbus_assert (list->n_items == 0);
85   _dbus_assert (list->items == NULL);
86
87   _dbus_loop_remove_timeout (list->loop, list->timeout,
88                              call_timeout_callback, NULL);
89
90   _dbus_timeout_unref (list->timeout);
91
92   dbus_free (list);
93 }
94
95 void
96 bus_expire_timeout_set_interval (DBusTimeout *timeout,
97                                  int          next_interval)
98 {
99   if (next_interval >= 0)
100     {
101       _dbus_timeout_set_interval (timeout,
102                                   next_interval);
103       _dbus_timeout_set_enabled (timeout, TRUE);
104
105       _dbus_verbose ("Enabled expire timeout with interval %d\n",
106                      next_interval);
107     }
108   else if (dbus_timeout_get_enabled (timeout))
109     {
110       _dbus_timeout_set_enabled (timeout, FALSE);
111
112       _dbus_verbose ("Disabled expire timeout\n");
113     }
114   else
115     _dbus_verbose ("No need to disable expire timeout\n");
116 }
117
118 static int
119 do_expiration_with_current_time (BusExpireList *list,
120                                  long           tv_sec,
121                                  long           tv_usec)
122 {
123   DBusList *link;
124   int next_interval;
125
126   next_interval = -1;
127   
128   link = _dbus_list_get_first_link (&list->items);
129   while (link != NULL)
130     {
131       DBusList *next = _dbus_list_get_next_link (&list->items, link);
132       double elapsed;
133       BusExpireItem *item;
134
135       item = link->data;
136
137       elapsed = ELAPSED_MILLISECONDS_SINCE (item->added_tv_sec,
138                                             item->added_tv_usec,
139                                             tv_sec, tv_usec);
140
141       if (elapsed >= (double) list->expire_after)
142         {
143           _dbus_verbose ("Expiring an item %p\n", item);
144           (* list->expire_func) (list, item, list->data);
145         }
146       else
147         {
148           /* We can end the loop, since the connections are in oldest-first order */
149           next_interval = ((double)list->expire_after) - elapsed;
150           _dbus_verbose ("Item %p expires in %d milliseconds\n",
151                          item, next_interval);
152
153           break;
154         }
155
156       link = next;
157     }
158
159   return next_interval;
160 }
161
162 static void
163 bus_expirelist_expire (BusExpireList *list)
164 {
165   int next_interval;
166
167   next_interval = -1;
168
169   if (list->items != NULL)
170     {
171       long tv_sec, tv_usec;
172
173       _dbus_get_current_time (&tv_sec, &tv_usec);
174
175       next_interval = do_expiration_with_current_time (list, tv_sec, tv_usec);
176     }
177
178   bus_expire_timeout_set_interval (list->timeout, next_interval);
179 }
180
181 static dbus_bool_t
182 expire_timeout_handler (void *data)
183 {
184   BusExpireList *list = data;
185
186   _dbus_verbose ("Running %s\n", _DBUS_FUNCTION_NAME);
187
188   /* note that this may remove the timeout */
189   bus_expirelist_expire (list);
190
191   return TRUE;
192 }
193
194 #ifdef DBUS_BUILD_TESTS
195
196 typedef struct
197 {
198   BusExpireItem item;
199   int expire_count;
200 } TestExpireItem;
201
202 static void
203 test_expire_func (BusExpireList *list,
204                   BusExpireItem *item,
205                   void          *data)
206 {
207   TestExpireItem *t;
208
209   t = (TestExpireItem*) item;
210
211   t->expire_count += 1;
212 }
213
214 static void
215 time_add_milliseconds (long *tv_sec,
216                        long *tv_usec,
217                        int   milliseconds)
218 {
219   *tv_sec = *tv_sec + milliseconds / 1000;
220   *tv_usec = *tv_usec + milliseconds * 1000;
221   if (*tv_usec >= 1000000)
222     {
223       *tv_usec -= 1000000;
224       *tv_sec += 1;
225     }
226 }
227
228 dbus_bool_t
229 bus_expire_list_test (const DBusString *test_data_dir)
230 {
231   DBusLoop *loop;
232   BusExpireList *list;
233   long tv_sec, tv_usec;
234   long tv_sec_not_expired, tv_usec_not_expired;
235   long tv_sec_expired, tv_usec_expired;
236   long tv_sec_past, tv_usec_past;
237   TestExpireItem *item;
238   int next_interval;
239   
240   loop = _dbus_loop_new ();
241   _dbus_assert (loop != NULL);
242
243 #define EXPIRE_AFTER 100
244   
245   list = bus_expire_list_new (loop, EXPIRE_AFTER,
246                               test_expire_func, NULL);
247   _dbus_assert (list != NULL);
248
249   _dbus_get_current_time (&tv_sec, &tv_usec);
250
251   tv_sec_not_expired = tv_sec;
252   tv_usec_not_expired = tv_usec;
253   time_add_milliseconds (&tv_sec_not_expired,
254                          &tv_usec_not_expired, EXPIRE_AFTER - 1);
255
256   tv_sec_expired = tv_sec;
257   tv_usec_expired = tv_usec;
258   time_add_milliseconds (&tv_sec_expired,
259                          &tv_usec_expired, EXPIRE_AFTER);
260   
261
262   tv_sec_past = tv_sec - 1;
263   tv_usec_past = tv_usec;
264
265   item = dbus_new0 (TestExpireItem, 1);
266
267   item->item.added_tv_sec = tv_sec;
268   item->item.added_tv_usec = tv_usec;
269   if (!_dbus_list_append (&list->items, item))
270     _dbus_assert_not_reached ("out of memory");
271
272   next_interval =
273     do_expiration_with_current_time (list, tv_sec_not_expired,
274                                      tv_usec_not_expired);
275   _dbus_assert (item->expire_count == 0);
276   _dbus_verbose ("next_interval = %d\n", next_interval);
277   _dbus_assert (next_interval == 1);
278   
279   next_interval =
280     do_expiration_with_current_time (list, tv_sec_expired,
281                                      tv_usec_expired);
282   _dbus_assert (item->expire_count == 1);
283   _dbus_verbose ("next_interval = %d\n", next_interval);
284   _dbus_assert (next_interval == -1);
285
286   next_interval =
287     do_expiration_with_current_time (list, tv_sec_past,
288                                      tv_usec_past);
289   _dbus_assert (item->expire_count == 1);
290   _dbus_verbose ("next_interval = %d\n", next_interval);
291   _dbus_assert (next_interval == 1000 + EXPIRE_AFTER);
292
293   _dbus_list_clear (&list->items);
294   dbus_free (item);
295   
296   bus_expire_list_free (list);
297   _dbus_loop_unref (loop);
298   
299   return TRUE;
300 }
301
302 #endif /* DBUS_BUILD_TESTS */