2003-04-07 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / dbus / dbus-mainloop.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-mainloop.c  Main loop utility
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 "dbus-mainloop.h"
25
26 #include <dbus/dbus-list.h>
27 #include <dbus/dbus-sysdeps.h>
28
29 struct DBusLoop
30 {
31   int refcount;
32   DBusList *callbacks;
33   int callback_list_serial;
34   int watch_count;
35   int timeout_count;
36   int depth; /**< number of recursive runs */
37 };
38
39 typedef enum
40 {
41   CALLBACK_WATCH,
42   CALLBACK_TIMEOUT
43 } CallbackType;
44
45 typedef struct
46 {
47   CallbackType type;
48   void *data;
49   DBusFreeFunction free_data_func;
50 } Callback;
51
52 typedef struct
53 {
54   Callback callback;
55   DBusWatchFunction function;
56   DBusWatch *watch;
57   /* last watch handle failed due to OOM */
58   unsigned int last_iteration_oom : 1;
59 } WatchCallback;
60
61 typedef struct
62 {
63   Callback callback;
64   DBusTimeout *timeout;
65   DBusTimeoutFunction function;
66   unsigned long last_tv_sec;
67   unsigned long last_tv_usec;
68 } TimeoutCallback;
69
70 #define WATCH_CALLBACK(callback)   ((WatchCallback*)callback)
71 #define TIMEOUT_CALLBACK(callback) ((TimeoutCallback*)callback)
72
73 static WatchCallback*
74 watch_callback_new (DBusWatch        *watch,
75                     DBusWatchFunction  function,
76                     void             *data,
77                     DBusFreeFunction  free_data_func)
78 {
79   WatchCallback *cb;
80
81   cb = dbus_new (WatchCallback, 1);
82   if (cb == NULL)
83     return NULL;
84
85   cb->watch = watch;
86   cb->function = function;
87   cb->last_iteration_oom = FALSE;
88   cb->callback.type = CALLBACK_WATCH;
89   cb->callback.data = data;
90   cb->callback.free_data_func = free_data_func;
91
92   return cb;
93 }
94
95 static TimeoutCallback*
96 timeout_callback_new (DBusTimeout        *timeout,
97                       DBusTimeoutFunction  function,
98                       void               *data,
99                       DBusFreeFunction    free_data_func)
100 {
101   TimeoutCallback *cb;
102
103   cb = dbus_new (TimeoutCallback, 1);
104   if (cb == NULL)
105     return NULL;
106
107   cb->timeout = timeout;
108   cb->function = function;
109   _dbus_get_current_time (&cb->last_tv_sec,
110                           &cb->last_tv_usec);
111   cb->callback.type = CALLBACK_TIMEOUT;
112   cb->callback.data = data;
113   cb->callback.free_data_func = free_data_func;
114   
115   return cb;
116 }
117
118 static void
119 callback_free (Callback *cb)
120 {
121   if (cb->free_data_func)
122     (* cb->free_data_func) (cb->data);
123
124   dbus_free (cb);
125 }
126
127 static dbus_bool_t
128 add_callback (DBusLoop  *loop,
129               Callback *cb)
130 {
131   if (!_dbus_list_append (&loop->callbacks, cb))
132     return FALSE;
133
134   loop->callback_list_serial += 1;
135
136   switch (cb->type)
137     {
138     case CALLBACK_WATCH:
139       loop->watch_count += 1;
140       break;
141     case CALLBACK_TIMEOUT:
142       loop->timeout_count += 1;
143       break;
144     }
145   
146   return TRUE;
147 }
148
149 static void
150 remove_callback (DBusLoop  *loop,
151                  DBusList *link)
152 {
153   Callback *cb = link->data;
154   
155   switch (cb->type)
156     {
157     case CALLBACK_WATCH:
158       loop->watch_count -= 1;
159       break;
160     case CALLBACK_TIMEOUT:
161       loop->timeout_count -= 1;
162       break;
163     }
164   
165   callback_free (cb);
166   _dbus_list_remove_link (&loop->callbacks, link);
167   loop->callback_list_serial += 1;
168 }
169
170 DBusLoop*
171 _dbus_loop_new (void)
172 {
173   DBusLoop *loop;
174
175   loop = dbus_new0 (DBusLoop, 1);
176   if (loop == NULL)
177     return NULL;
178
179   loop->refcount = 1;
180   
181   return loop;
182 }
183
184 void
185 _dbus_loop_ref (DBusLoop *loop)
186 {
187   _dbus_assert (loop != NULL);
188   _dbus_assert (loop->refcount > 0);
189
190   loop->refcount += 1;
191 }
192
193 void
194 _dbus_loop_unref (DBusLoop *loop)
195 {
196   _dbus_assert (loop != NULL);
197   _dbus_assert (loop->refcount > 0);
198
199   loop->refcount -= 1;
200   if (loop->refcount == 0)
201     {
202       
203       dbus_free (loop);
204     }
205 }
206
207 dbus_bool_t
208 _dbus_loop_add_watch (DBusLoop          *loop,
209                       DBusWatch        *watch,
210                       DBusWatchFunction  function,
211                       void             *data,
212                       DBusFreeFunction  free_data_func)
213 {
214   WatchCallback *wcb;
215
216   wcb = watch_callback_new (watch, function, data, free_data_func);
217   if (wcb == NULL)
218     return FALSE;
219
220   if (!add_callback (loop, (Callback*) wcb))
221     {
222       wcb->callback.free_data_func = NULL; /* don't want to have this side effect */
223       callback_free ((Callback*) wcb);
224       return FALSE;
225     }
226   
227   return TRUE;
228 }
229
230 void
231 _dbus_loop_remove_watch (DBusLoop          *loop,
232                          DBusWatch        *watch,
233                          DBusWatchFunction  function,
234                          void             *data)
235 {
236   DBusList *link;
237   
238   link = _dbus_list_get_first_link (&loop->callbacks);
239   while (link != NULL)
240     {
241       DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
242       Callback *this = link->data;
243
244       if (this->type == CALLBACK_WATCH &&
245           WATCH_CALLBACK (this)->watch == watch &&
246           this->data == data &&
247           WATCH_CALLBACK (this)->function == function)
248         {
249           remove_callback (loop, link);
250           
251           return;
252         }
253       
254       link = next;
255     }
256
257   _dbus_warn ("could not find watch %p function %p data %p to remove\n",
258               watch, function, data);
259 }
260
261 dbus_bool_t
262 _dbus_loop_add_timeout (DBusLoop            *loop,
263                         DBusTimeout        *timeout,
264                         DBusTimeoutFunction  function,
265                         void               *data,
266                         DBusFreeFunction    free_data_func)
267 {
268   TimeoutCallback *tcb;
269
270   tcb = timeout_callback_new (timeout, function, data, free_data_func);
271   if (tcb == NULL)
272     return FALSE;
273
274   if (!add_callback (loop, (Callback*) tcb))
275     {
276       tcb->callback.free_data_func = NULL; /* don't want to have this side effect */
277       callback_free ((Callback*) tcb);
278       return FALSE;
279     }
280   
281   return TRUE;
282 }
283
284 void
285 _dbus_loop_remove_timeout (DBusLoop            *loop,
286                            DBusTimeout        *timeout,
287                            DBusTimeoutFunction  function,
288                            void               *data)
289 {
290   DBusList *link;
291   
292   link = _dbus_list_get_first_link (&loop->callbacks);
293   while (link != NULL)
294     {
295       DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
296       Callback *this = link->data;
297
298       if (this->type == CALLBACK_TIMEOUT &&
299           TIMEOUT_CALLBACK (this)->timeout == timeout &&
300           this->data == data &&
301           TIMEOUT_CALLBACK (this)->function == function)
302         {
303           remove_callback (loop, link);
304           
305           return;
306         }
307       
308       link = next;
309     }
310
311   _dbus_warn ("could not find timeout %p function %p data %p to remove\n",
312               timeout, function, data);
313 }
314
315 /* Convolutions from GLib, there really must be a better way
316  * to do this.
317  */
318 static dbus_bool_t
319 check_timeout (unsigned long    tv_sec,
320                unsigned long    tv_usec,
321                TimeoutCallback *tcb,
322                int             *timeout)
323 {
324   long sec;
325   long msec;
326   unsigned long expiration_tv_sec;
327   unsigned long expiration_tv_usec;
328   long interval_seconds;
329   long interval_milliseconds;
330   int interval;
331
332   interval = dbus_timeout_get_interval (tcb->timeout);
333   
334   interval_seconds = interval / 1000;
335   interval_milliseconds = interval - interval_seconds * 1000;
336   
337   expiration_tv_sec = tcb->last_tv_sec + interval_seconds;
338   expiration_tv_usec = tcb->last_tv_usec + interval_milliseconds * 1000;
339   if (expiration_tv_usec >= 1000000)
340     {
341       expiration_tv_usec -= 1000000;
342       expiration_tv_sec += 1;
343     }
344   
345   sec = expiration_tv_sec - tv_sec;
346   msec = (expiration_tv_usec - tv_usec) / 1000;
347
348 #if 0
349   printf ("Interval is %ld seconds %ld msecs\n",
350           interval_seconds,
351           interval_milliseconds);
352   printf ("Now is %lu seconds %lu usecs\n",
353           tv_sec, tv_usec);
354   printf ("Exp is %lu seconds %lu usecs\n",
355           expiration_tv_sec, expiration_tv_usec);
356   printf ("Pre-correction, remaining sec %ld msec %ld\n", sec, msec);
357 #endif
358   
359   /* We do the following in a rather convoluted fashion to deal with
360    * the fact that we don't have an integral type big enough to hold
361    * the difference of two timevals in millseconds.
362    */
363   if (sec < 0 || (sec == 0 && msec < 0))
364     msec = 0;
365   else
366     {
367       if (msec < 0)
368         {
369           msec += 1000;
370           sec -= 1;
371         }
372       
373       if (sec > interval_seconds ||
374           (sec == interval_seconds && msec > interval_milliseconds))
375         {
376           _dbus_verbose ("System clock went backward interval_seconds %ld interval_msecs %ld sec %ld msec %ld last_tv_sec %lu last_tv_usec %lu tv_sec %lu tv_usec %lu\n",
377                          interval_seconds, interval_milliseconds, sec, msec, tcb->last_tv_sec,
378                          tcb->last_tv_usec, tv_sec, tv_usec);
379           
380           /* The system time has been set backwards, reset the timeout */          
381           
382           tcb->last_tv_sec = tv_sec;
383           tcb->last_tv_usec = tv_usec;
384           
385           msec = MIN (_DBUS_INT_MAX, interval);
386         }
387       else
388         {
389           msec = MIN (_DBUS_INT_MAX, (unsigned int)msec + 1000 * (unsigned int)sec);
390         }
391     }
392
393   *timeout = msec;
394
395 #if 0
396   printf ("Timeout expires in %d milliseconds\n", *timeout);
397 #endif
398   
399   return msec == 0;
400 }
401
402 /* Returns TRUE if we have any timeouts or ready file descriptors,
403  * which is just used in test code as a debug hack
404  */
405
406 dbus_bool_t
407 _dbus_loop_iterate (DBusLoop     *loop,
408                     dbus_bool_t  block)
409 {
410   dbus_bool_t retval;
411   DBusPollFD *fds;
412   int n_fds;
413   WatchCallback **watches_for_fds;
414   int i;
415   DBusList *link;
416   int n_ready;
417   int initial_serial;
418   long timeout;
419   dbus_bool_t oom_watch_pending;
420   int orig_depth;
421   
422   retval = FALSE;
423       
424   fds = NULL;
425   watches_for_fds = NULL;
426   oom_watch_pending = FALSE;
427   orig_depth = loop->depth;
428   
429 #if 0
430   _dbus_verbose (" iterate %d timeouts %d watches\n",
431                  loop->timeout_count, loop->watch_count);
432 #endif
433   
434   if (loop->callbacks == NULL)
435     {
436       _dbus_loop_quit (loop);
437       goto next_iteration;
438     }
439
440   /* count enabled watches */
441   n_fds = 0;
442   link = _dbus_list_get_first_link (&loop->callbacks);
443   while (link != NULL)
444     {
445       DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
446       Callback *cb = link->data;
447       if (cb->type == CALLBACK_WATCH)
448         {
449           WatchCallback *wcb = WATCH_CALLBACK (cb);
450
451           if (!wcb->last_iteration_oom &&
452               dbus_watch_get_enabled (wcb->watch))
453             ++n_fds;
454         }
455       
456       link = next;
457     }
458
459   /* fill our array of fds and watches */
460   if (n_fds > 0)
461     {
462       fds = dbus_new0 (DBusPollFD, n_fds);
463       while (fds == NULL)
464         {
465           _dbus_wait_for_memory ();
466           fds = dbus_new0 (DBusPollFD, n_fds);
467         }
468           
469       watches_for_fds = dbus_new (WatchCallback*, n_fds);
470       while (watches_for_fds == NULL)
471         {
472           _dbus_wait_for_memory ();
473           watches_for_fds = dbus_new (WatchCallback*, n_fds);
474         }
475       
476       i = 0;
477       link = _dbus_list_get_first_link (&loop->callbacks);
478       while (link != NULL)
479         {
480           DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
481           Callback *cb = link->data;
482           if (cb->type == CALLBACK_WATCH)
483             {
484               unsigned int flags;
485               WatchCallback *wcb = WATCH_CALLBACK (cb);
486
487               if (wcb->last_iteration_oom)
488                 {
489                   /* we skip this one this time, but reenable it next time,
490                    * and have a timeout on this iteration
491                    */
492                   wcb->last_iteration_oom = FALSE;
493                   oom_watch_pending = TRUE;
494                 }
495               else if (dbus_watch_get_enabled (wcb->watch))
496                 {
497                   watches_for_fds[i] = wcb;
498                   
499                   flags = dbus_watch_get_flags (wcb->watch);
500                   
501                   fds[i].fd = dbus_watch_get_fd (wcb->watch);
502                   if (flags & DBUS_WATCH_READABLE)
503                     fds[i].events |= _DBUS_POLLIN;
504                   if (flags & DBUS_WATCH_WRITABLE)
505                     fds[i].events |= _DBUS_POLLOUT;
506
507                   ++i;
508                 }
509             }
510               
511           link = next;
512         }
513
514       _dbus_assert (i == n_fds);
515     }
516
517   timeout = -1;
518   if (loop->timeout_count > 0)
519     {
520       unsigned long tv_sec;
521       unsigned long tv_usec;
522
523       retval = TRUE;
524       
525       _dbus_get_current_time (&tv_sec, &tv_usec);
526           
527       link = _dbus_list_get_first_link (&loop->callbacks);
528       while (link != NULL)
529         {
530           DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
531           Callback *cb = link->data;
532
533           if (cb->type == CALLBACK_TIMEOUT &&
534               dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout))
535             {
536               TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb);
537               int msecs_remaining;
538
539               check_timeout (tv_sec, tv_usec, tcb, &msecs_remaining);
540
541               if (timeout < 0)
542                 timeout = msecs_remaining;
543               else
544                 timeout = MIN (msecs_remaining, timeout);
545               
546               _dbus_assert (timeout >= 0);
547                   
548               if (timeout == 0)
549                 break; /* it's not going to get shorter... */
550             }
551               
552           link = next;
553         }
554     }
555
556   if (!block)
557     {
558       timeout = 0;
559 #if 0
560       printf ("timeout is 0 as we aren't blocking\n");
561 #endif
562     }
563
564   /* if a watch is OOM, don't wait longer than the OOM
565    * wait to re-enable it
566    */
567   if (oom_watch_pending)
568     timeout = MIN (timeout, _dbus_get_oom_wait ());
569   
570   n_ready = _dbus_poll (fds, n_fds, timeout);
571
572   initial_serial = loop->callback_list_serial;
573
574   if (loop->timeout_count > 0)
575     {
576       unsigned long tv_sec;
577       unsigned long tv_usec;
578
579       _dbus_get_current_time (&tv_sec, &tv_usec);
580
581       /* It'd be nice to avoid this O(n) thingy here */
582       link = _dbus_list_get_first_link (&loop->callbacks);
583       while (link != NULL)
584         {
585           DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
586           Callback *cb = link->data;
587
588           if (initial_serial != loop->callback_list_serial)
589             goto next_iteration;
590
591           if (loop->depth != orig_depth)
592             goto next_iteration;
593               
594           if (cb->type == CALLBACK_TIMEOUT &&
595               dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout))
596             {
597               TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb);
598               int msecs_remaining;
599               
600               if (check_timeout (tv_sec, tv_usec,
601                                  tcb, &msecs_remaining))
602                 {
603                   /* Save last callback time and fire this timeout */
604                   tcb->last_tv_sec = tv_sec;
605                   tcb->last_tv_usec = tv_usec;
606
607 #if 0
608                   printf ("  invoking timeout\n");
609 #endif
610                   
611                   (* tcb->function) (tcb->timeout,
612                                      cb->data);
613                 }
614             }
615
616           link = next;
617         }
618     }
619       
620   if (n_ready > 0)
621     {
622       i = 0;
623       while (i < n_fds)
624         {
625           /* FIXME I think this "restart if we change the watches"
626            * approach could result in starving watches
627            * toward the end of the list.
628            */
629           if (initial_serial != loop->callback_list_serial)
630             goto next_iteration;
631
632           if (loop->depth != orig_depth)
633             goto next_iteration;
634
635           if (fds[i].revents != 0)
636             {
637               WatchCallback *wcb;
638               unsigned int condition;
639                   
640               wcb = watches_for_fds[i];
641                   
642               condition = 0;
643               if (fds[i].revents & _DBUS_POLLIN)
644                 condition |= DBUS_WATCH_READABLE;
645               if (fds[i].revents & _DBUS_POLLOUT)
646                 condition |= DBUS_WATCH_WRITABLE;
647               if (fds[i].revents & _DBUS_POLLHUP)
648                 condition |= DBUS_WATCH_HANGUP;
649               if (fds[i].revents & _DBUS_POLLERR)
650                 condition |= DBUS_WATCH_ERROR;
651
652               /* condition may still be 0 if we got some
653                * weird POLLFOO thing like POLLWRBAND
654                */
655                   
656               if (condition != 0 &&
657                   dbus_watch_get_enabled (wcb->watch))
658                 {
659                   if (!(* wcb->function) (wcb->watch,
660                                           condition,
661                                           ((Callback*)wcb)->data))
662                     wcb->last_iteration_oom = TRUE;
663
664                   retval = TRUE;
665                 }
666             }
667               
668           ++i;
669         }
670     }
671       
672  next_iteration:
673   dbus_free (fds);
674   dbus_free (watches_for_fds);
675
676   return retval;
677 }
678
679 void
680 _dbus_loop_run (DBusLoop *loop)
681 {
682   int our_exit_depth;
683
684   _dbus_loop_ref (loop);
685   
686   our_exit_depth = loop->depth;
687   loop->depth += 1;
688   
689   while (loop->depth != our_exit_depth)
690     _dbus_loop_iterate (loop, TRUE);
691
692   _dbus_loop_unref (loop);
693 }
694
695 void
696 _dbus_loop_quit (DBusLoop *loop)
697 {
698   _dbus_assert (loop->depth > 0);
699   
700   loop->depth -= 1;
701 }
702
703 int
704 _dbus_get_oom_wait (void)
705 {
706 #ifdef DBUS_BUILD_TESTS
707   /* make tests go fast */
708   return 0;
709 #else
710   return 500;
711 #endif
712 }
713
714 void
715 _dbus_wait_for_memory (void)
716 {
717   _dbus_sleep_milliseconds (_dbus_get_oom_wait ());
718 }