dbus-mainloop.c revision 7caf646fdf595946eb28202e2df3f847d28c7151
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
29struct 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
40typedef enum
41{
42  CALLBACK_WATCH,
43  CALLBACK_TIMEOUT
44} CallbackType;
45
46typedef struct
47{
48  CallbackType type;
49  void *data;
50  DBusFreeFunction free_data_func;
51} Callback;
52
53typedef 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
62typedef 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
74static WatchCallback*
75watch_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
96static TimeoutCallback*
97timeout_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
119static void
120callback_free (Callback *cb)
121{
122  if (cb->free_data_func)
123    (* cb->free_data_func) (cb->data);
124
125  dbus_free (cb);
126}
127
128static dbus_bool_t
129add_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
150static void
151remove_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
171DBusLoop*
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
185void
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
194void
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
214dbus_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
237void
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
268dbus_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
291void
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 */
325static dbus_bool_t
326check_timeout (unsigned long    tv_sec,
327               unsigned long    tv_usec,
328               TimeoutCallback *tcb,
329               int             *timeout)
330{
331  long sec;
332  long msec;
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  sec = expiration_tv_sec - tv_sec;
353  msec = (expiration_tv_usec - tv_usec) / 1000;
354
355#if 0
356  printf ("Interval is %ld seconds %ld msecs\n",
357          interval_seconds,
358          interval_milliseconds);
359  printf ("Now is %lu seconds %lu usecs\n",
360          tv_sec, tv_usec);
361  printf ("Exp is %lu seconds %lu usecs\n",
362          expiration_tv_sec, expiration_tv_usec);
363  printf ("Pre-correction, remaining sec %ld msec %ld\n", sec, msec);
364#endif
365
366  /* We do the following in a rather convoluted fashion to deal with
367   * the fact that we don't have an integral type big enough to hold
368   * the difference of two timevals in millseconds.
369   */
370  if (sec < 0 || (sec == 0 && msec < 0))
371    msec = 0;
372  else
373    {
374      if (msec < 0)
375	{
376	  msec += 1000;
377	  sec -= 1;
378	}
379
380      if (sec > interval_seconds ||
381	  (sec == interval_seconds && msec > interval_milliseconds))
382	{
383          _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",
384                         interval_seconds, interval_milliseconds, sec, msec, tcb->last_tv_sec,
385                         tcb->last_tv_usec, tv_sec, tv_usec);
386
387	  /* The system time has been set backwards, reset the timeout */
388
389          tcb->last_tv_sec = tv_sec;
390          tcb->last_tv_usec = tv_usec;
391
392          msec = MIN (_DBUS_INT_MAX, interval);
393	}
394      else
395	{
396	  msec = MIN (_DBUS_INT_MAX, (unsigned int)msec + 1000 * (unsigned int)sec);
397	}
398    }
399
400  *timeout = msec;
401
402#if 0
403  printf ("Timeout expires in %d milliseconds\n", *timeout);
404#endif
405
406  return msec == 0;
407}
408
409static void
410_dbus_loop_dispatch (DBusLoop *loop)
411{
412 next:
413  while (loop->need_dispatch != NULL)
414    {
415      DBusConnection *connection = _dbus_list_pop_first (&loop->need_dispatch);
416
417      while (TRUE)
418        {
419          DBusDispatchStatus status;
420
421          status = dbus_connection_dispatch (connection);
422
423          if (status == DBUS_DISPATCH_COMPLETE)
424            {
425              dbus_connection_unref (connection);
426              goto next;
427            }
428          else
429            {
430              if (status == DBUS_DISPATCH_NEED_MEMORY)
431                _dbus_wait_for_memory ();
432            }
433        }
434    }
435}
436
437dbus_bool_t
438_dbus_loop_queue_dispatch (DBusLoop       *loop,
439                           DBusConnection *connection)
440{
441
442  if (_dbus_list_append (&loop->need_dispatch, connection))
443    {
444      dbus_connection_ref (connection);
445      return TRUE;
446    }
447  else
448    return FALSE;
449}
450
451/* Returns TRUE if we have any timeouts or ready file descriptors,
452 * which is just used in test code as a debug hack
453 */
454
455dbus_bool_t
456_dbus_loop_iterate (DBusLoop     *loop,
457                    dbus_bool_t   block)
458{
459  dbus_bool_t retval;
460  DBusPollFD *fds;
461  int n_fds;
462  WatchCallback **watches_for_fds;
463  int i;
464  DBusList *link;
465  int n_ready;
466  int initial_serial;
467  long timeout;
468  dbus_bool_t oom_watch_pending;
469  int orig_depth;
470
471  retval = FALSE;
472
473  fds = NULL;
474  watches_for_fds = NULL;
475  oom_watch_pending = FALSE;
476  orig_depth = loop->depth;
477
478#if 0
479  _dbus_verbose (" iterate %d timeouts %d watches\n",
480                 loop->timeout_count, loop->watch_count);
481#endif
482
483  if (loop->callbacks == NULL)
484    {
485      _dbus_loop_quit (loop);
486      goto next_iteration;
487    }
488
489  /* count enabled watches */
490  n_fds = 0;
491  link = _dbus_list_get_first_link (&loop->callbacks);
492  while (link != NULL)
493    {
494      DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
495      Callback *cb = link->data;
496      if (cb->type == CALLBACK_WATCH)
497        {
498          WatchCallback *wcb = WATCH_CALLBACK (cb);
499
500          if (!wcb->last_iteration_oom &&
501              dbus_watch_get_enabled (wcb->watch))
502            ++n_fds;
503        }
504
505      link = next;
506    }
507
508  /* fill our array of fds and watches */
509  if (n_fds > 0)
510    {
511      fds = dbus_new0 (DBusPollFD, n_fds);
512      while (fds == NULL)
513        {
514          _dbus_wait_for_memory ();
515          fds = dbus_new0 (DBusPollFD, n_fds);
516        }
517
518      watches_for_fds = dbus_new (WatchCallback*, n_fds);
519      while (watches_for_fds == NULL)
520        {
521          _dbus_wait_for_memory ();
522          watches_for_fds = dbus_new (WatchCallback*, n_fds);
523        }
524
525      i = 0;
526      link = _dbus_list_get_first_link (&loop->callbacks);
527      while (link != NULL)
528        {
529          DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
530          Callback *cb = link->data;
531          if (cb->type == CALLBACK_WATCH)
532            {
533              unsigned int flags;
534              WatchCallback *wcb = WATCH_CALLBACK (cb);
535
536              if (wcb->last_iteration_oom)
537                {
538                  /* we skip this one this time, but reenable it next time,
539                   * and have a timeout on this iteration
540                   */
541                  wcb->last_iteration_oom = FALSE;
542                  oom_watch_pending = TRUE;
543                }
544              else if (dbus_watch_get_enabled (wcb->watch))
545                {
546                  watches_for_fds[i] = wcb;
547
548                  flags = dbus_watch_get_flags (wcb->watch);
549
550                  fds[i].fd = dbus_watch_get_fd (wcb->watch);
551                  if (flags & DBUS_WATCH_READABLE)
552                    fds[i].events |= _DBUS_POLLIN;
553                  if (flags & DBUS_WATCH_WRITABLE)
554                    fds[i].events |= _DBUS_POLLOUT;
555
556                  ++i;
557                }
558            }
559
560          link = next;
561        }
562
563      _dbus_assert (i == n_fds);
564    }
565
566  timeout = -1;
567  if (loop->timeout_count > 0)
568    {
569      unsigned long tv_sec;
570      unsigned long tv_usec;
571
572      retval = TRUE;
573
574      _dbus_get_current_time (&tv_sec, &tv_usec);
575
576      link = _dbus_list_get_first_link (&loop->callbacks);
577      while (link != NULL)
578        {
579          DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
580          Callback *cb = link->data;
581
582          if (cb->type == CALLBACK_TIMEOUT &&
583              dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout))
584            {
585              TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb);
586              int msecs_remaining;
587
588              check_timeout (tv_sec, tv_usec, tcb, &msecs_remaining);
589
590              if (timeout < 0)
591                timeout = msecs_remaining;
592              else
593                timeout = MIN (msecs_remaining, timeout);
594
595              _dbus_assert (timeout >= 0);
596
597              if (timeout == 0)
598                break; /* it's not going to get shorter... */
599            }
600
601          link = next;
602        }
603    }
604
605  /* Never block if we have stuff to dispatch */
606  if (!block || loop->need_dispatch != NULL)
607    {
608      timeout = 0;
609#if 0
610      printf ("timeout is 0 as we aren't blocking\n");
611#endif
612    }
613
614  /* if a watch is OOM, don't wait longer than the OOM
615   * wait to re-enable it
616   */
617  if (oom_watch_pending)
618    timeout = MIN (timeout, _dbus_get_oom_wait ());
619
620  n_ready = _dbus_poll (fds, n_fds, timeout);
621
622  initial_serial = loop->callback_list_serial;
623
624  if (loop->timeout_count > 0)
625    {
626      unsigned long tv_sec;
627      unsigned long tv_usec;
628
629      _dbus_get_current_time (&tv_sec, &tv_usec);
630
631      /* It'd be nice to avoid this O(n) thingy here */
632      link = _dbus_list_get_first_link (&loop->callbacks);
633      while (link != NULL)
634        {
635          DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
636          Callback *cb = link->data;
637
638          if (initial_serial != loop->callback_list_serial)
639            goto next_iteration;
640
641          if (loop->depth != orig_depth)
642            goto next_iteration;
643
644          if (cb->type == CALLBACK_TIMEOUT &&
645              dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout))
646            {
647              TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb);
648              int msecs_remaining;
649
650              if (check_timeout (tv_sec, tv_usec,
651                                 tcb, &msecs_remaining))
652                {
653                  /* Save last callback time and fire this timeout */
654                  tcb->last_tv_sec = tv_sec;
655                  tcb->last_tv_usec = tv_usec;
656
657#if 0
658                  printf ("  invoking timeout\n");
659#endif
660
661                  (* tcb->function) (tcb->timeout,
662                                     cb->data);
663                }
664            }
665
666          link = next;
667        }
668    }
669
670  if (n_ready > 0)
671    {
672      i = 0;
673      while (i < n_fds)
674        {
675          /* FIXME I think this "restart if we change the watches"
676           * approach could result in starving watches
677           * toward the end of the list.
678           */
679          if (initial_serial != loop->callback_list_serial)
680            goto next_iteration;
681
682          if (loop->depth != orig_depth)
683            goto next_iteration;
684
685          if (fds[i].revents != 0)
686            {
687              WatchCallback *wcb;
688              unsigned int condition;
689
690              wcb = watches_for_fds[i];
691
692              condition = 0;
693              if (fds[i].revents & _DBUS_POLLIN)
694                condition |= DBUS_WATCH_READABLE;
695              if (fds[i].revents & _DBUS_POLLOUT)
696                condition |= DBUS_WATCH_WRITABLE;
697              if (fds[i].revents & _DBUS_POLLHUP)
698                condition |= DBUS_WATCH_HANGUP;
699              if (fds[i].revents & _DBUS_POLLERR)
700                condition |= DBUS_WATCH_ERROR;
701
702              /* condition may still be 0 if we got some
703               * weird POLLFOO thing like POLLWRBAND
704               */
705
706              if (condition != 0 &&
707                  dbus_watch_get_enabled (wcb->watch))
708                {
709                  if (!(* wcb->function) (wcb->watch,
710                                          condition,
711                                          ((Callback*)wcb)->data))
712                    wcb->last_iteration_oom = TRUE;
713
714                  retval = TRUE;
715                }
716            }
717
718          ++i;
719        }
720    }
721
722 next_iteration:
723  dbus_free (fds);
724  dbus_free (watches_for_fds);
725
726  if (loop->need_dispatch != NULL)
727    {
728      retval = TRUE;
729      _dbus_loop_dispatch (loop);
730    }
731
732  return retval;
733}
734
735void
736_dbus_loop_run (DBusLoop *loop)
737{
738  int our_exit_depth;
739
740  _dbus_loop_ref (loop);
741
742  our_exit_depth = loop->depth;
743  loop->depth += 1;
744
745  while (loop->depth != our_exit_depth)
746    _dbus_loop_iterate (loop, TRUE);
747
748  _dbus_loop_unref (loop);
749}
750
751void
752_dbus_loop_quit (DBusLoop *loop)
753{
754  _dbus_assert (loop->depth > 0);
755
756  loop->depth -= 1;
757}
758
759int
760_dbus_get_oom_wait (void)
761{
762#ifdef DBUS_BUILD_TESTS
763  /* make tests go fast */
764  return 0;
765#else
766  return 500;
767#endif
768}
769
770void
771_dbus_wait_for_memory (void)
772{
773  _dbus_sleep_milliseconds (_dbus_get_oom_wait ());
774}
775
776