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