• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
39 static const char*
watch_flags_to_string(int flags)40 watch_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 
58 struct 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 
75 typedef 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 
84 static TimeoutCallback*
timeout_callback_new(DBusTimeout * timeout)85 timeout_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 
99 static void
timeout_callback_free(TimeoutCallback * cb)100 timeout_callback_free (TimeoutCallback *cb)
101 {
102   dbus_free (cb);
103 }
104 
105 static void
free_watch_table_entry(void * data)106 free_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 
127 DBusLoop*
_dbus_loop_new(void)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 
158 DBusLoop *
_dbus_loop_ref(DBusLoop * loop)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 
169 void
_dbus_loop_unref(DBusLoop * loop)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 
191 static DBusList **
ensure_watch_table_entry(DBusLoop * loop,int fd)192 ensure_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 
216 static void
cull_watches_for_invalid_fd(DBusLoop * loop,int fd)217 cull_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 
237 static dbus_bool_t
gc_watch_table_entry(DBusLoop * loop,DBusList ** watches,int fd)238 gc_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 
254 static void
refresh_watches_for_fd(DBusLoop * loop,DBusList ** watches,int fd)255 refresh_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 
290 dbus_bool_t
_dbus_loop_add_watch(DBusLoop * loop,DBusWatch * watch)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 
334 void
_dbus_loop_toggle_watch(DBusLoop * loop,DBusWatch * watch)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 
341 void
_dbus_loop_remove_watch(DBusLoop * loop,DBusWatch * watch)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 
389 dbus_bool_t
_dbus_loop_add_timeout(DBusLoop * loop,DBusTimeout * timeout)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 
413 void
_dbus_loop_remove_timeout(DBusLoop * loop,DBusTimeout * timeout)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  */
444 static dbus_bool_t
check_timeout(unsigned long tv_sec,unsigned long tv_usec,TimeoutCallback * tcb,int * timeout)445 check_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 
534 dbus_bool_t
_dbus_loop_dispatch(DBusLoop * loop)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 
572 dbus_bool_t
_dbus_loop_queue_dispatch(DBusLoop * loop,DBusConnection * connection)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 
589 dbus_bool_t
_dbus_loop_iterate(DBusLoop * loop,dbus_bool_t block)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 
890 void
_dbus_loop_run(DBusLoop * loop)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 
911 void
_dbus_loop_quit(DBusLoop * loop)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 
922 int
_dbus_get_oom_wait(void)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 
933 void
_dbus_wait_for_memory(void)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