1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2/* dbus-mainloop.c  Main loop utility
3 *
4 * Copyright © 2003, 2004  Red Hat, Inc.
5 * Copyright © 2011 Nokia Corporation
6 *
7 * Licensed under the Academic Free License version 2.1
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 */
24
25#include <config.h>
26#include "dbus-mainloop.h"
27
28#ifndef DOXYGEN_SHOULD_SKIP_THIS
29
30#include <dbus/dbus-hash.h>
31#include <dbus/dbus-list.h>
32#include <dbus/dbus-socket-set.h>
33#include <dbus/dbus-watch.h>
34
35#define MAINLOOP_SPEW 0
36
37#if MAINLOOP_SPEW
38#ifdef DBUS_ENABLE_VERBOSE_MODE
39static const char*
40watch_flags_to_string (int flags)
41{
42  const char *watch_type;
43
44  if ((flags & DBUS_WATCH_READABLE) &&
45      (flags & DBUS_WATCH_WRITABLE))
46    watch_type = "readwrite";
47  else if (flags & DBUS_WATCH_READABLE)
48    watch_type = "read";
49  else if (flags & DBUS_WATCH_WRITABLE)
50    watch_type = "write";
51  else
52    watch_type = "not read or write";
53  return watch_type;
54}
55#endif /* DBUS_ENABLE_VERBOSE_MODE */
56#endif /* MAINLOOP_SPEW */
57
58struct DBusLoop
59{
60  int refcount;
61  /** fd => dbus_malloc'd DBusList ** of references to DBusWatch */
62  DBusHashTable *watches;
63  DBusSocketSet *socket_set;
64  DBusList *timeouts;
65  int callback_list_serial;
66  int watch_count;
67  int timeout_count;
68  int depth; /**< number of recursive runs */
69  DBusList *need_dispatch;
70  /** TRUE if we will skip a watch next time because it was OOM; becomes
71   * FALSE between polling, and dealing with the results of the poll */
72  unsigned oom_watch_pending : 1;
73};
74
75typedef struct
76{
77  DBusTimeout *timeout;
78  unsigned long last_tv_sec;
79  unsigned long last_tv_usec;
80} TimeoutCallback;
81
82#define TIMEOUT_CALLBACK(callback) ((TimeoutCallback*)callback)
83
84static TimeoutCallback*
85timeout_callback_new (DBusTimeout         *timeout)
86{
87  TimeoutCallback *cb;
88
89  cb = dbus_new (TimeoutCallback, 1);
90  if (cb == NULL)
91    return NULL;
92
93  cb->timeout = timeout;
94  _dbus_get_monotonic_time (&cb->last_tv_sec,
95                            &cb->last_tv_usec);
96  return cb;
97}
98
99static void
100timeout_callback_free (TimeoutCallback *cb)
101{
102  dbus_free (cb);
103}
104
105static void
106free_watch_table_entry (void *data)
107{
108  DBusList **watches = data;
109  DBusWatch *watch;
110
111  /* DBusHashTable sometimes calls free_function(NULL) even if you never
112   * have NULL as a value */
113  if (watches == NULL)
114    return;
115
116  for (watch = _dbus_list_pop_first (watches);
117      watch != NULL;
118      watch = _dbus_list_pop_first (watches))
119    {
120      _dbus_watch_unref (watch);
121    }
122
123  _dbus_assert (*watches == NULL);
124  dbus_free (watches);
125}
126
127DBusLoop*
128_dbus_loop_new (void)
129{
130  DBusLoop *loop;
131
132  loop = dbus_new0 (DBusLoop, 1);
133  if (loop == NULL)
134    return NULL;
135
136  loop->watches = _dbus_hash_table_new (DBUS_HASH_INT, NULL,
137                                        free_watch_table_entry);
138
139  loop->socket_set = _dbus_socket_set_new (0);
140
141  if (loop->watches == NULL || loop->socket_set == NULL)
142    {
143      if (loop->watches != NULL)
144        _dbus_hash_table_unref (loop->watches);
145
146      if (loop->socket_set != NULL)
147        _dbus_socket_set_free (loop->socket_set);
148
149      dbus_free (loop);
150      return NULL;
151    }
152
153  loop->refcount = 1;
154
155  return loop;
156}
157
158DBusLoop *
159_dbus_loop_ref (DBusLoop *loop)
160{
161  _dbus_assert (loop != NULL);
162  _dbus_assert (loop->refcount > 0);
163
164  loop->refcount += 1;
165
166  return loop;
167}
168
169void
170_dbus_loop_unref (DBusLoop *loop)
171{
172  _dbus_assert (loop != NULL);
173  _dbus_assert (loop->refcount > 0);
174
175  loop->refcount -= 1;
176  if (loop->refcount == 0)
177    {
178      while (loop->need_dispatch)
179        {
180          DBusConnection *connection = _dbus_list_pop_first (&loop->need_dispatch);
181
182          dbus_connection_unref (connection);
183        }
184
185      _dbus_hash_table_unref (loop->watches);
186      _dbus_socket_set_free (loop->socket_set);
187      dbus_free (loop);
188    }
189}
190
191static DBusList **
192ensure_watch_table_entry (DBusLoop *loop,
193                          int       fd)
194{
195  DBusList **watches;
196
197  watches = _dbus_hash_table_lookup_int (loop->watches, fd);
198
199  if (watches == NULL)
200    {
201      watches = dbus_new0 (DBusList *, 1);
202
203      if (watches == NULL)
204        return watches;
205
206      if (!_dbus_hash_table_insert_int (loop->watches, fd, watches))
207        {
208          dbus_free (watches);
209          watches = NULL;
210        }
211    }
212
213  return watches;
214}
215
216static void
217cull_watches_for_invalid_fd (DBusLoop  *loop,
218                             int        fd)
219{
220  DBusList *link;
221  DBusList **watches;
222
223  _dbus_warn ("invalid request, socket fd %d not open\n", fd);
224  watches = _dbus_hash_table_lookup_int (loop->watches, fd);
225
226  if (watches != NULL)
227    {
228      for (link = _dbus_list_get_first_link (watches);
229          link != NULL;
230          link = _dbus_list_get_next_link (watches, link))
231        _dbus_watch_invalidate (link->data);
232    }
233
234  _dbus_hash_table_remove_int (loop->watches, fd);
235}
236
237static dbus_bool_t
238gc_watch_table_entry (DBusLoop  *loop,
239                      DBusList **watches,
240                      int        fd)
241{
242  /* If watches is already NULL we have nothing to do */
243  if (watches == NULL)
244    return FALSE;
245
246  /* We can't GC hash table entries if they're non-empty lists */
247  if (*watches != NULL)
248    return FALSE;
249
250  _dbus_hash_table_remove_int (loop->watches, fd);
251  return TRUE;
252}
253
254static void
255refresh_watches_for_fd (DBusLoop  *loop,
256                        DBusList **watches,
257                        int        fd)
258{
259  DBusList *link;
260  unsigned int flags = 0;
261  dbus_bool_t interested = FALSE;
262
263  _dbus_assert (fd != -1);
264
265  if (watches == NULL)
266    watches = _dbus_hash_table_lookup_int (loop->watches, fd);
267
268  /* we allocated this in the first _dbus_loop_add_watch for the fd, and keep
269   * it until there are none left */
270  _dbus_assert (watches != NULL);
271
272  for (link = _dbus_list_get_first_link (watches);
273      link != NULL;
274      link = _dbus_list_get_next_link (watches, link))
275    {
276      if (dbus_watch_get_enabled (link->data) &&
277          !_dbus_watch_get_oom_last_time (link->data))
278        {
279          flags |= dbus_watch_get_flags (link->data);
280          interested = TRUE;
281        }
282    }
283
284  if (interested)
285    _dbus_socket_set_enable (loop->socket_set, fd, flags);
286  else
287    _dbus_socket_set_disable (loop->socket_set, fd);
288}
289
290dbus_bool_t
291_dbus_loop_add_watch (DBusLoop  *loop,
292                      DBusWatch *watch)
293{
294  int fd;
295  DBusList **watches;
296
297  fd = dbus_watch_get_socket (watch);
298  _dbus_assert (fd != -1);
299
300  watches = ensure_watch_table_entry (loop, fd);
301
302  if (watches == NULL)
303    return FALSE;
304
305  if (!_dbus_list_append (watches, _dbus_watch_ref (watch)))
306    {
307      _dbus_watch_unref (watch);
308      gc_watch_table_entry (loop, watches, fd);
309
310      return FALSE;
311    }
312
313  if (_dbus_list_length_is_one (watches))
314    {
315      if (!_dbus_socket_set_add (loop->socket_set, fd,
316                                 dbus_watch_get_flags (watch),
317                                 dbus_watch_get_enabled (watch)))
318        {
319          _dbus_hash_table_remove_int (loop->watches, fd);
320          return FALSE;
321        }
322    }
323  else
324    {
325      /* we're modifying, not adding, which can't fail with OOM */
326      refresh_watches_for_fd (loop, watches, fd);
327    }
328
329  loop->callback_list_serial += 1;
330  loop->watch_count += 1;
331  return TRUE;
332}
333
334void
335_dbus_loop_toggle_watch (DBusLoop          *loop,
336                         DBusWatch         *watch)
337{
338  refresh_watches_for_fd (loop, NULL, dbus_watch_get_socket (watch));
339}
340
341void
342_dbus_loop_remove_watch (DBusLoop         *loop,
343                         DBusWatch        *watch)
344{
345  DBusList **watches;
346  DBusList *link;
347  int fd;
348
349  /* This relies on people removing watches before they invalidate them,
350   * which has been safe since fd.o #33336 was fixed. Assert about it
351   * so we don't regress. */
352  fd = dbus_watch_get_socket (watch);
353  _dbus_assert (fd != -1);
354
355  watches = _dbus_hash_table_lookup_int (loop->watches, fd);
356
357  if (watches != NULL)
358    {
359      link = _dbus_list_get_first_link (watches);
360      while (link != NULL)
361        {
362          DBusList *next = _dbus_list_get_next_link (watches, link);
363          DBusWatch *this = link->data;
364
365          if (this == watch)
366            {
367              _dbus_list_remove_link (watches, link);
368              loop->callback_list_serial += 1;
369              loop->watch_count -= 1;
370              _dbus_watch_unref (this);
371
372              /* if that was the last watch for that fd, drop the hash table
373               * entry, and stop reserving space for it in the socket set */
374              if (gc_watch_table_entry (loop, watches, fd))
375                {
376                  _dbus_socket_set_remove (loop->socket_set, fd);
377                }
378
379              return;
380            }
381
382          link = next;
383         }
384     }
385
386  _dbus_warn ("could not find watch %p to remove\n", watch);
387}
388
389dbus_bool_t
390_dbus_loop_add_timeout (DBusLoop           *loop,
391                        DBusTimeout        *timeout)
392{
393  TimeoutCallback *tcb;
394
395  tcb = timeout_callback_new (timeout);
396  if (tcb == NULL)
397    return FALSE;
398
399  if (_dbus_list_append (&loop->timeouts, tcb))
400    {
401      loop->callback_list_serial += 1;
402      loop->timeout_count += 1;
403    }
404  else
405    {
406      timeout_callback_free (tcb);
407      return FALSE;
408    }
409
410  return TRUE;
411}
412
413void
414_dbus_loop_remove_timeout (DBusLoop           *loop,
415                           DBusTimeout        *timeout)
416{
417  DBusList *link;
418
419  link = _dbus_list_get_first_link (&loop->timeouts);
420  while (link != NULL)
421    {
422      DBusList *next = _dbus_list_get_next_link (&loop->timeouts, link);
423      TimeoutCallback *this = link->data;
424
425      if (this->timeout == timeout)
426        {
427          _dbus_list_remove_link (&loop->timeouts, link);
428          loop->callback_list_serial += 1;
429          loop->timeout_count -= 1;
430          timeout_callback_free (this);
431
432          return;
433        }
434
435      link = next;
436    }
437
438  _dbus_warn ("could not find timeout %p to remove\n", timeout);
439}
440
441/* Convolutions from GLib, there really must be a better way
442 * to do this.
443 */
444static dbus_bool_t
445check_timeout (unsigned long    tv_sec,
446               unsigned long    tv_usec,
447               TimeoutCallback *tcb,
448               int             *timeout)
449{
450  long sec_remaining;
451  long msec_remaining;
452  unsigned long expiration_tv_sec;
453  unsigned long expiration_tv_usec;
454  long interval_seconds;
455  long interval_milliseconds;
456  int interval;
457
458  /* I'm pretty sure this function could suck (a lot) less */
459
460  interval = dbus_timeout_get_interval (tcb->timeout);
461
462  interval_seconds = interval / 1000L;
463  interval_milliseconds = interval % 1000L;
464
465  expiration_tv_sec = tcb->last_tv_sec + interval_seconds;
466  expiration_tv_usec = tcb->last_tv_usec + interval_milliseconds * 1000;
467  if (expiration_tv_usec >= 1000000)
468    {
469      expiration_tv_usec -= 1000000;
470      expiration_tv_sec += 1;
471    }
472
473  sec_remaining = expiration_tv_sec - tv_sec;
474  /* need to force this to be signed, as it is intended to sometimes
475   * produce a negative result
476   */
477  msec_remaining = ((long) expiration_tv_usec - (long) tv_usec) / 1000L;
478
479#if MAINLOOP_SPEW
480  _dbus_verbose ("Interval is %ld seconds %ld msecs\n",
481                 interval_seconds,
482                 interval_milliseconds);
483  _dbus_verbose ("Now is  %lu seconds %lu usecs\n",
484                 tv_sec, tv_usec);
485  _dbus_verbose ("Last is %lu seconds %lu usecs\n",
486                 tcb->last_tv_sec, tcb->last_tv_usec);
487  _dbus_verbose ("Exp is  %lu seconds %lu usecs\n",
488                 expiration_tv_sec, expiration_tv_usec);
489  _dbus_verbose ("Pre-correction, sec_remaining %ld msec_remaining %ld\n",
490                 sec_remaining, msec_remaining);
491#endif
492
493  /* We do the following in a rather convoluted fashion to deal with
494   * the fact that we don't have an integral type big enough to hold
495   * the difference of two timevals in milliseconds.
496   */
497  if (sec_remaining < 0 || (sec_remaining == 0 && msec_remaining < 0))
498    {
499      *timeout = 0;
500    }
501  else
502    {
503      if (msec_remaining < 0)
504	{
505	  msec_remaining += 1000;
506	  sec_remaining -= 1;
507	}
508
509      if (sec_remaining > (_DBUS_INT_MAX / 1000) ||
510          msec_remaining > _DBUS_INT_MAX)
511        *timeout = _DBUS_INT_MAX;
512      else
513        *timeout = sec_remaining * 1000 + msec_remaining;
514    }
515
516  if (*timeout > interval)
517    {
518      /* This indicates that the system clock probably moved backward */
519      _dbus_verbose ("System clock set backward! Resetting timeout.\n");
520
521      tcb->last_tv_sec = tv_sec;
522      tcb->last_tv_usec = tv_usec;
523
524      *timeout = interval;
525    }
526
527#if MAINLOOP_SPEW
528  _dbus_verbose ("  timeout expires in %d milliseconds\n", *timeout);
529#endif
530
531  return *timeout == 0;
532}
533
534dbus_bool_t
535_dbus_loop_dispatch (DBusLoop *loop)
536{
537
538#if MAINLOOP_SPEW
539  _dbus_verbose ("  %d connections to dispatch\n", _dbus_list_get_length (&loop->need_dispatch));
540#endif
541
542  if (loop->need_dispatch == NULL)
543    return FALSE;
544
545 next:
546  while (loop->need_dispatch != NULL)
547    {
548      DBusConnection *connection = _dbus_list_pop_first (&loop->need_dispatch);
549
550      while (TRUE)
551        {
552          DBusDispatchStatus status;
553
554          status = dbus_connection_dispatch (connection);
555
556          if (status == DBUS_DISPATCH_COMPLETE)
557            {
558              dbus_connection_unref (connection);
559              goto next;
560            }
561          else
562            {
563              if (status == DBUS_DISPATCH_NEED_MEMORY)
564                _dbus_wait_for_memory ();
565            }
566        }
567    }
568
569  return TRUE;
570}
571
572dbus_bool_t
573_dbus_loop_queue_dispatch (DBusLoop       *loop,
574                           DBusConnection *connection)
575{
576  if (_dbus_list_append (&loop->need_dispatch, connection))
577    {
578      dbus_connection_ref (connection);
579      return TRUE;
580    }
581  else
582    return FALSE;
583}
584
585/* Returns TRUE if we invoked any timeouts or have ready file
586 * descriptors, which is just used in test code as a debug hack
587 */
588
589dbus_bool_t
590_dbus_loop_iterate (DBusLoop     *loop,
591                    dbus_bool_t   block)
592{
593#define N_STACK_DESCRIPTORS 64
594  dbus_bool_t retval;
595  DBusSocketEvent ready_fds[N_STACK_DESCRIPTORS];
596  int i;
597  DBusList *link;
598  int n_ready;
599  int initial_serial;
600  long timeout;
601  int orig_depth;
602
603  retval = FALSE;
604
605  orig_depth = loop->depth;
606
607#if MAINLOOP_SPEW
608  _dbus_verbose ("Iteration block=%d depth=%d timeout_count=%d watch_count=%d\n",
609                 block, loop->depth, loop->timeout_count, loop->watch_count);
610#endif
611
612  if (_dbus_hash_table_get_n_entries (loop->watches) == 0 &&
613      loop->timeouts == NULL)
614    goto next_iteration;
615
616  timeout = -1;
617  if (loop->timeout_count > 0)
618    {
619      unsigned long tv_sec;
620      unsigned long tv_usec;
621
622      _dbus_get_monotonic_time (&tv_sec, &tv_usec);
623
624      link = _dbus_list_get_first_link (&loop->timeouts);
625      while (link != NULL)
626        {
627          DBusList *next = _dbus_list_get_next_link (&loop->timeouts, link);
628          TimeoutCallback *tcb = link->data;
629
630          if (dbus_timeout_get_enabled (tcb->timeout))
631            {
632              int msecs_remaining;
633
634              check_timeout (tv_sec, tv_usec, tcb, &msecs_remaining);
635
636              if (timeout < 0)
637                timeout = msecs_remaining;
638              else
639                timeout = MIN (msecs_remaining, timeout);
640
641#if MAINLOOP_SPEW
642              _dbus_verbose ("  timeout added, %d remaining, aggregate timeout %ld\n",
643                             msecs_remaining, timeout);
644#endif
645
646              _dbus_assert (timeout >= 0);
647
648              if (timeout == 0)
649                break; /* it's not going to get shorter... */
650            }
651#if MAINLOOP_SPEW
652          else
653            {
654              _dbus_verbose ("  skipping disabled timeout\n");
655            }
656#endif
657
658          link = next;
659        }
660    }
661
662  /* Never block if we have stuff to dispatch */
663  if (!block || loop->need_dispatch != NULL)
664    {
665      timeout = 0;
666#if MAINLOOP_SPEW
667      _dbus_verbose ("  timeout is 0 as we aren't blocking\n");
668#endif
669    }
670
671  /* if a watch was OOM last time, don't wait longer than the OOM
672   * wait to re-enable it
673   */
674  if (loop->oom_watch_pending)
675    timeout = MIN (timeout, _dbus_get_oom_wait ());
676
677#if MAINLOOP_SPEW
678  _dbus_verbose ("  polling on %d descriptors timeout %ld\n", n_fds, timeout);
679#endif
680
681  n_ready = _dbus_socket_set_poll (loop->socket_set, ready_fds,
682                                   _DBUS_N_ELEMENTS (ready_fds), timeout);
683
684  /* re-enable any watches we skipped this time */
685  if (loop->oom_watch_pending)
686    {
687      DBusHashIter hash_iter;
688
689      loop->oom_watch_pending = FALSE;
690
691      _dbus_hash_iter_init (loop->watches, &hash_iter);
692
693      while (_dbus_hash_iter_next (&hash_iter))
694        {
695          DBusList **watches;
696          int fd;
697          dbus_bool_t changed;
698
699          changed = FALSE;
700          fd = _dbus_hash_iter_get_int_key (&hash_iter);
701          watches = _dbus_hash_iter_get_value (&hash_iter);
702
703          for (link = _dbus_list_get_first_link (watches);
704              link != NULL;
705              link = _dbus_list_get_next_link (watches, link))
706            {
707              DBusWatch *watch = link->data;
708
709              if (_dbus_watch_get_oom_last_time (watch))
710                {
711                  _dbus_watch_set_oom_last_time (watch, FALSE);
712                  changed = TRUE;
713                }
714            }
715
716          if (changed)
717            refresh_watches_for_fd (loop, watches, fd);
718        }
719
720      retval = TRUE; /* return TRUE here to keep the loop going,
721                      * since we don't know the watch was inactive */
722    }
723
724  initial_serial = loop->callback_list_serial;
725
726  if (loop->timeout_count > 0)
727    {
728      unsigned long tv_sec;
729      unsigned long tv_usec;
730
731      _dbus_get_monotonic_time (&tv_sec, &tv_usec);
732
733      /* It'd be nice to avoid this O(n) thingy here */
734      link = _dbus_list_get_first_link (&loop->timeouts);
735      while (link != NULL)
736        {
737          DBusList *next = _dbus_list_get_next_link (&loop->timeouts, link);
738          TimeoutCallback *tcb = link->data;
739
740          if (initial_serial != loop->callback_list_serial)
741            goto next_iteration;
742
743          if (loop->depth != orig_depth)
744            goto next_iteration;
745
746          if (dbus_timeout_get_enabled (tcb->timeout))
747            {
748              int msecs_remaining;
749
750              if (check_timeout (tv_sec, tv_usec,
751                                 tcb, &msecs_remaining))
752                {
753                  /* Save last callback time and fire this timeout */
754                  tcb->last_tv_sec = tv_sec;
755                  tcb->last_tv_usec = tv_usec;
756
757#if MAINLOOP_SPEW
758                  _dbus_verbose ("  invoking timeout\n");
759#endif
760
761                  /* can theoretically return FALSE on OOM, but we just
762                   * let it fire again later - in practice that's what
763                   * every wrapper callback in dbus-daemon used to do */
764                  dbus_timeout_handle (tcb->timeout);
765
766                  retval = TRUE;
767                }
768              else
769                {
770#if MAINLOOP_SPEW
771                  _dbus_verbose ("  timeout has not expired\n");
772#endif
773                }
774            }
775#if MAINLOOP_SPEW
776          else
777            {
778              _dbus_verbose ("  skipping invocation of disabled timeout\n");
779            }
780#endif
781
782          link = next;
783        }
784    }
785
786  if (n_ready > 0)
787    {
788      for (i = 0; i < n_ready; i++)
789        {
790          DBusList **watches;
791          DBusList *next;
792          unsigned int condition;
793          dbus_bool_t any_oom;
794
795          /* FIXME I think this "restart if we change the watches"
796           * approach could result in starving watches
797           * toward the end of the list.
798           */
799          if (initial_serial != loop->callback_list_serial)
800            goto next_iteration;
801
802          if (loop->depth != orig_depth)
803            goto next_iteration;
804
805          _dbus_assert (ready_fds[i].flags != 0);
806
807          if (_DBUS_UNLIKELY (ready_fds[i].flags & _DBUS_WATCH_NVAL))
808            {
809              cull_watches_for_invalid_fd (loop, ready_fds[i].fd);
810              goto next_iteration;
811            }
812
813          condition = ready_fds[i].flags;
814          _dbus_assert ((condition & _DBUS_WATCH_NVAL) == 0);
815
816          /* condition may still be 0 if we got some
817           * weird POLLFOO thing like POLLWRBAND
818           */
819          if (condition == 0)
820            continue;
821
822          watches = _dbus_hash_table_lookup_int (loop->watches,
823                                                 ready_fds[i].fd);
824
825          if (watches == NULL)
826            continue;
827
828          any_oom = FALSE;
829
830          for (link = _dbus_list_get_first_link (watches);
831              link != NULL;
832              link = next)
833            {
834              DBusWatch *watch = link->data;
835
836              next = _dbus_list_get_next_link (watches, link);
837
838              if (dbus_watch_get_enabled (watch))
839                {
840                  dbus_bool_t oom;
841
842                  oom = !dbus_watch_handle (watch, condition);
843
844                  if (oom)
845                    {
846                      _dbus_watch_set_oom_last_time (watch, TRUE);
847                      loop->oom_watch_pending = TRUE;
848                      any_oom = TRUE;
849                    }
850
851#if MAINLOOP_SPEW
852                  _dbus_verbose ("  Invoked watch, oom = %d\n", oom);
853#endif
854                  retval = TRUE;
855
856                  /* We re-check this every time, in case the callback
857                   * added/removed watches, which might make our position in
858                   * the linked list invalid. See the FIXME above. */
859                  if (initial_serial != loop->callback_list_serial ||
860                      loop->depth != orig_depth)
861                    {
862                      if (any_oom)
863                        refresh_watches_for_fd (loop, NULL, ready_fds[i].fd);
864
865                      goto next_iteration;
866                    }
867                }
868            }
869
870          if (any_oom)
871            refresh_watches_for_fd (loop, watches, ready_fds[i].fd);
872        }
873    }
874
875 next_iteration:
876#if MAINLOOP_SPEW
877  _dbus_verbose ("  moving to next iteration\n");
878#endif
879
880  if (_dbus_loop_dispatch (loop))
881    retval = TRUE;
882
883#if MAINLOOP_SPEW
884  _dbus_verbose ("Returning %d\n", retval);
885#endif
886
887  return retval;
888}
889
890void
891_dbus_loop_run (DBusLoop *loop)
892{
893  int our_exit_depth;
894
895  _dbus_assert (loop->depth >= 0);
896
897  _dbus_loop_ref (loop);
898
899  our_exit_depth = loop->depth;
900  loop->depth += 1;
901
902  _dbus_verbose ("Running main loop, depth %d -> %d\n",
903                 loop->depth - 1, loop->depth);
904
905  while (loop->depth != our_exit_depth)
906    _dbus_loop_iterate (loop, TRUE);
907
908  _dbus_loop_unref (loop);
909}
910
911void
912_dbus_loop_quit (DBusLoop *loop)
913{
914  _dbus_assert (loop->depth > 0);
915
916  loop->depth -= 1;
917
918  _dbus_verbose ("Quit main loop, depth %d -> %d\n",
919                 loop->depth + 1, loop->depth);
920}
921
922int
923_dbus_get_oom_wait (void)
924{
925#ifdef DBUS_BUILD_TESTS
926  /* make tests go fast */
927  return 0;
928#else
929  return 500;
930#endif
931}
932
933void
934_dbus_wait_for_memory (void)
935{
936  _dbus_verbose ("Waiting for more memory\n");
937  _dbus_sleep_milliseconds (_dbus_get_oom_wait ());
938}
939
940#endif /* !DOXYGEN_SHOULD_SKIP_THIS */
941