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