[rename] renamed kdbus related macros
[platform/upstream/dbus.git] / dbus / dbus-mainloop.c
index b49c9b2..8b4da16 100644 (file)
@@ -1,7 +1,8 @@
 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 /* dbus-mainloop.c  Main loop utility
  *
- * Copyright (C) 2003, 2004  Red Hat, Inc.
+ * Copyright © 2003, 2004  Red Hat, Inc.
+ * Copyright © 2011 Nokia Corporation
  *
  * Licensed under the Academic Free License version 2.1
  * 
 
 #include <dbus/dbus-hash.h>
 #include <dbus/dbus-list.h>
-#include <dbus/dbus-sysdeps.h>
+#include <dbus/dbus-socket-set.h>
 #include <dbus/dbus-watch.h>
 
 #define MAINLOOP_SPEW 0
 
-#if MAINLOOP_SPEW
-#ifdef DBUS_ENABLE_VERBOSE_MODE
-static const char*
-watch_flags_to_string (int flags)
-{
-  const char *watch_type;
-
-  if ((flags & DBUS_WATCH_READABLE) &&
-      (flags & DBUS_WATCH_WRITABLE))
-    watch_type = "readwrite";
-  else if (flags & DBUS_WATCH_READABLE)
-    watch_type = "read";
-  else if (flags & DBUS_WATCH_WRITABLE)
-    watch_type = "write";
-  else
-    watch_type = "not read or write";
-  return watch_type;
-}
-#endif /* DBUS_ENABLE_VERBOSE_MODE */
-#endif /* MAINLOOP_SPEW */
-
 struct DBusLoop
 {
   int refcount;
   /** fd => dbus_malloc'd DBusList ** of references to DBusWatch */
   DBusHashTable *watches;
+  DBusSocketSet *socket_set;
   DBusList *timeouts;
   int callback_list_serial;
   int watch_count;
   int timeout_count;
   int depth; /**< number of recursive runs */
   DBusList *need_dispatch;
+  /** TRUE if we will skip a watch next time because it was OOM; becomes
+   * FALSE between polling, and dealing with the results of the poll */
+  unsigned oom_watch_pending : 1;
 };
 
-static short
-watch_flags_to_poll_events (unsigned int flags)
-{
-  short events = 0;
-
-  if (flags & DBUS_WATCH_READABLE)
-    events |= _DBUS_POLLIN;
-  if (flags & DBUS_WATCH_WRITABLE)
-    events |= _DBUS_POLLOUT;
-
-  return events;
-}
-
-static unsigned int
-watch_flags_from_poll_revents (short revents)
-{
-  unsigned int condition = 0;
-
-  if (revents & _DBUS_POLLIN)
-    condition |= DBUS_WATCH_READABLE;
-  if (revents & _DBUS_POLLOUT)
-    condition |= DBUS_WATCH_WRITABLE;
-  if (revents & _DBUS_POLLHUP)
-    condition |= DBUS_WATCH_HANGUP;
-  if (revents & _DBUS_POLLERR)
-    condition |= DBUS_WATCH_ERROR;
-
-  return condition;
-}
-
 typedef struct
 {
-  int refcount;
   DBusTimeout *timeout;
   unsigned long last_tv_sec;
   unsigned long last_tv_usec;
@@ -117,33 +70,15 @@ timeout_callback_new (DBusTimeout         *timeout)
     return NULL;
 
   cb->timeout = timeout;
-  _dbus_get_current_time (&cb->last_tv_sec,
-                          &cb->last_tv_usec);
-  cb->refcount = 1;
-  return cb;
-}
-
-static TimeoutCallback *
-timeout_callback_ref (TimeoutCallback *cb)
-{
-  _dbus_assert (cb->refcount > 0);
-  
-  cb->refcount += 1;
-
+  _dbus_get_monotonic_time (&cb->last_tv_sec,
+                            &cb->last_tv_usec);
   return cb;
 }
 
 static void
-timeout_callback_unref (TimeoutCallback *cb)
+timeout_callback_free (TimeoutCallback *cb)
 {
-  _dbus_assert (cb->refcount > 0);
-
-  cb->refcount -= 1;
-
-  if (cb->refcount == 0)
-    {
-      dbus_free (cb);
-    }
+  dbus_free (cb);
 }
 
 static void
@@ -180,8 +115,16 @@ _dbus_loop_new (void)
   loop->watches = _dbus_hash_table_new (DBUS_HASH_INT, NULL,
                                         free_watch_table_entry);
 
-  if (loop->watches == NULL)
+  loop->socket_set = _dbus_socket_set_new (0);
+
+  if (loop->watches == NULL || loop->socket_set == NULL)
     {
+      if (loop->watches != NULL)
+        _dbus_hash_table_unref (loop->watches);
+
+      if (loop->socket_set != NULL)
+        _dbus_socket_set_free (loop->socket_set);
+
       dbus_free (loop);
       return NULL;
     }
@@ -219,6 +162,7 @@ _dbus_loop_unref (DBusLoop *loop)
         }
 
       _dbus_hash_table_unref (loop->watches);
+      _dbus_socket_set_free (loop->socket_set);
       dbus_free (loop);
     }
 }
@@ -253,7 +197,6 @@ cull_watches_for_invalid_fd (DBusLoop  *loop,
                              int        fd)
 {
   DBusList *link;
-  DBusList *next;
   DBusList **watches;
 
   _dbus_warn ("invalid request, socket fd %d not open\n", fd);
@@ -270,20 +213,57 @@ cull_watches_for_invalid_fd (DBusLoop  *loop,
   _dbus_hash_table_remove_int (loop->watches, fd);
 }
 
-static void
+static dbus_bool_t
 gc_watch_table_entry (DBusLoop  *loop,
                       DBusList **watches,
                       int        fd)
 {
   /* If watches is already NULL we have nothing to do */
   if (watches == NULL)
-    return;
+    return FALSE;
 
   /* We can't GC hash table entries if they're non-empty lists */
   if (*watches != NULL)
-    return;
+    return FALSE;
 
   _dbus_hash_table_remove_int (loop->watches, fd);
+  return TRUE;
+}
+
+static void
+refresh_watches_for_fd (DBusLoop  *loop,
+                        DBusList **watches,
+                        int        fd)
+{
+  DBusList *link;
+  unsigned int flags = 0;
+  dbus_bool_t interested = FALSE;
+
+  _dbus_assert (fd != -1);
+
+  if (watches == NULL)
+    watches = _dbus_hash_table_lookup_int (loop->watches, fd);
+
+  /* we allocated this in the first _dbus_loop_add_watch for the fd, and keep
+   * it until there are none left */
+  _dbus_assert (watches != NULL);
+
+  for (link = _dbus_list_get_first_link (watches);
+      link != NULL;
+      link = _dbus_list_get_next_link (watches, link))
+    {
+      if (dbus_watch_get_enabled (link->data) &&
+          !_dbus_watch_get_oom_last_time (link->data))
+        {
+          flags |= dbus_watch_get_flags (link->data);
+          interested = TRUE;
+        }
+    }
+
+  if (interested)
+    _dbus_socket_set_enable (loop->socket_set, fd, flags);
+  else
+    _dbus_socket_set_disable (loop->socket_set, fd);
 }
 
 dbus_bool_t
@@ -301,23 +281,43 @@ _dbus_loop_add_watch (DBusLoop  *loop,
   if (watches == NULL)
     return FALSE;
 
-  if (_dbus_list_append (watches, _dbus_watch_ref (watch)))
-    {
-      loop->callback_list_serial += 1;
-      loop->watch_count += 1;
-    }
-  else
+  if (!_dbus_list_append (watches, _dbus_watch_ref (watch)))
     {
       _dbus_watch_unref (watch);
       gc_watch_table_entry (loop, watches, fd);
 
-       return FALSE;
-     }
+      return FALSE;
+    }
+
+  if (_dbus_list_length_is_one (watches))
+    {
+      if (!_dbus_socket_set_add (loop->socket_set, fd,
+                                 dbus_watch_get_flags (watch),
+                                 dbus_watch_get_enabled (watch)))
+        {
+          _dbus_hash_table_remove_int (loop->watches, fd);
+          return FALSE;
+        }
+    }
+  else
+    {
+      /* we're modifying, not adding, which can't fail with OOM */
+      refresh_watches_for_fd (loop, watches, fd);
+    }
 
+  loop->callback_list_serial += 1;
+  loop->watch_count += 1;
   return TRUE;
 }
 
 void
+_dbus_loop_toggle_watch (DBusLoop          *loop,
+                         DBusWatch         *watch)
+{
+  refresh_watches_for_fd (loop, NULL, dbus_watch_get_socket (watch));
+}
+
+void
 _dbus_loop_remove_watch (DBusLoop         *loop,
                          DBusWatch        *watch)
 {
@@ -349,8 +349,11 @@ _dbus_loop_remove_watch (DBusLoop         *loop,
               _dbus_watch_unref (this);
 
               /* if that was the last watch for that fd, drop the hash table
-               * entry too */
-              gc_watch_table_entry (loop, watches, fd);
+               * entry, and stop reserving space for it in the socket set */
+              if (gc_watch_table_entry (loop, watches, fd))
+                {
+                  _dbus_socket_set_remove (loop->socket_set, fd);
+                }
 
               return;
             }
@@ -379,7 +382,7 @@ _dbus_loop_add_timeout (DBusLoop           *loop,
     }
   else
     {
-      timeout_callback_unref (tcb);
+      timeout_callback_free (tcb);
       return FALSE;
     }
   
@@ -403,7 +406,7 @@ _dbus_loop_remove_timeout (DBusLoop           *loop,
           _dbus_list_remove_link (&loop->timeouts, link);
           loop->callback_list_serial += 1;
           loop->timeout_count -= 1;
-          timeout_callback_unref (this);
+          timeout_callback_free (this);
 
           return;
         }
@@ -568,23 +571,16 @@ _dbus_loop_iterate (DBusLoop     *loop,
 {  
 #define N_STACK_DESCRIPTORS 64
   dbus_bool_t retval;
-  DBusPollFD *fds;
-  DBusPollFD stack_fds[N_STACK_DESCRIPTORS];
-  int n_fds;
+  DBusSocketEvent ready_fds[N_STACK_DESCRIPTORS];
   int i;
   DBusList *link;
   int n_ready;
   int initial_serial;
   long timeout;
-  dbus_bool_t oom_watch_pending;
   int orig_depth;
-  DBusHashIter hash_iter;
 
   retval = FALSE;      
 
-  fds = NULL;
-  n_fds = 0;
-  oom_watch_pending = FALSE;
   orig_depth = loop->depth;
   
 #if MAINLOOP_SPEW
@@ -596,94 +592,13 @@ _dbus_loop_iterate (DBusLoop     *loop,
       loop->timeouts == NULL)
     goto next_iteration;
 
-  if (loop->watch_count > N_STACK_DESCRIPTORS)
-    {
-      fds = dbus_new0 (DBusPollFD, loop->watch_count);
-      
-      while (fds == NULL)
-        {
-          _dbus_wait_for_memory ();
-          fds = dbus_new0 (DBusPollFD, loop->watch_count);
-        }
-    }
-  else
-    {      
-      fds = stack_fds;
-    }
-
-  /* fill our array of fds and watches */
-  n_fds = 0;
-  _dbus_hash_iter_init (loop->watches, &hash_iter);
-
-  while (_dbus_hash_iter_next (&hash_iter))
-    {
-      DBusList **watches;
-      unsigned int flags;
-      int fd;
-
-      fd = _dbus_hash_iter_get_int_key (&hash_iter);
-      watches = _dbus_hash_iter_get_value (&hash_iter);
-      flags = 0;
-
-      for (link = _dbus_list_get_first_link (watches);
-          link != NULL;
-          link = _dbus_list_get_next_link (watches, link))
-        {
-          DBusWatch *watch = link->data;
-
-          if (_dbus_watch_get_oom_last_time (watch))
-            {
-              /* we skip this one this time, but reenable it next time,
-               * and have a timeout on this iteration
-               */
-              _dbus_watch_set_oom_last_time (watch, FALSE);
-              oom_watch_pending = TRUE;
-
-              retval = TRUE; /* return TRUE here to keep the loop going,
-                              * since we don't know the watch is inactive
-                              */
-
-#if MAINLOOP_SPEW
-              _dbus_verbose ("  skipping watch on fd %d as it was out of memory last time\n",
-                             fd);
-#endif
-            }
-          else if (dbus_watch_get_enabled (watch))
-            {
-              flags |= dbus_watch_get_flags (watch);
-            }
-        }
-
-      if (flags != 0)
-        {
-          fds[n_fds].fd = fd;
-          fds[n_fds].revents = 0;
-          fds[n_fds].events = watch_flags_to_poll_events (flags);
-
-#if MAINLOOP_SPEW
-          _dbus_verbose ("  polling watch on fd %d  %s\n",
-                         loop->fds[loop->n_fds].fd, watch_flags_to_string (flags));
-#endif
-
-          n_fds += 1;
-        }
-      else
-        {
-#if MAINLOOP_SPEW
-          _dbus_verbose ("  skipping disabled watch on fd %d  %s\n",
-                         fd,
-                         watch_flags_to_string (dbus_watch_get_flags (watch)));
-#endif
-        }
-    }
-
   timeout = -1;
   if (loop->timeout_count > 0)
     {
       unsigned long tv_sec;
       unsigned long tv_usec;
       
-      _dbus_get_current_time (&tv_sec, &tv_usec);
+      _dbus_get_monotonic_time (&tv_sec, &tv_usec);
 
       link = _dbus_list_get_first_link (&loop->timeouts);
       while (link != NULL)
@@ -732,17 +647,58 @@ _dbus_loop_iterate (DBusLoop     *loop,
 #endif
     }
 
-  /* if a watch is OOM, don't wait longer than the OOM
+  /* if a watch was OOM last time, don't wait longer than the OOM
    * wait to re-enable it
    */
-  if (oom_watch_pending)
+  if (loop->oom_watch_pending)
     timeout = MIN (timeout, _dbus_get_oom_wait ());
 
 #if MAINLOOP_SPEW
   _dbus_verbose ("  polling on %d descriptors timeout %ld\n", n_fds, timeout);
 #endif
-  
-  n_ready = _dbus_poll (fds, n_fds, timeout);
+
+  n_ready = _dbus_socket_set_poll (loop->socket_set, ready_fds,
+                                   _DBUS_N_ELEMENTS (ready_fds), timeout);
+
+  /* re-enable any watches we skipped this time */
+  if (loop->oom_watch_pending)
+    {
+      DBusHashIter hash_iter;
+
+      loop->oom_watch_pending = FALSE;
+
+      _dbus_hash_iter_init (loop->watches, &hash_iter);
+
+      while (_dbus_hash_iter_next (&hash_iter))
+        {
+          DBusList **watches;
+          int fd;
+          dbus_bool_t changed;
+
+          changed = FALSE;
+          fd = _dbus_hash_iter_get_int_key (&hash_iter);
+          watches = _dbus_hash_iter_get_value (&hash_iter);
+
+          for (link = _dbus_list_get_first_link (watches);
+              link != NULL;
+              link = _dbus_list_get_next_link (watches, link))
+            {
+              DBusWatch *watch = link->data;
+
+              if (_dbus_watch_get_oom_last_time (watch))
+                {
+                  _dbus_watch_set_oom_last_time (watch, FALSE);
+                  changed = TRUE;
+                }
+            }
+
+          if (changed)
+            refresh_watches_for_fd (loop, watches, fd);
+        }
+
+      retval = TRUE; /* return TRUE here to keep the loop going,
+                      * since we don't know the watch was inactive */
+    }
 
   initial_serial = loop->callback_list_serial;
 
@@ -751,7 +707,7 @@ _dbus_loop_iterate (DBusLoop     *loop,
       unsigned long tv_sec;
       unsigned long tv_usec;
 
-      _dbus_get_current_time (&tv_sec, &tv_usec);
+      _dbus_get_monotonic_time (&tv_sec, &tv_usec);
 
       /* It'd be nice to avoid this O(n) thingy here */
       link = _dbus_list_get_first_link (&loop->timeouts);
@@ -805,14 +761,15 @@ _dbus_loop_iterate (DBusLoop     *loop,
           link = next;
         }
     }
-      
+
   if (n_ready > 0)
     {
-      for (i = 0; i < n_fds; i++)
+      for (i = 0; i < n_ready; i++)
         {
           DBusList **watches;
           DBusList *next;
           unsigned int condition;
+          dbus_bool_t any_oom;
 
           /* FIXME I think this "restart if we change the watches"
            * approach could result in starving watches
@@ -824,16 +781,16 @@ _dbus_loop_iterate (DBusLoop     *loop,
           if (loop->depth != orig_depth)
             goto next_iteration;
 
-          if (fds[i].revents == 0)
-            continue;
+          _dbus_assert (ready_fds[i].flags != 0);
 
-          if (_DBUS_UNLIKELY (fds[i].revents & _DBUS_POLLNVAL))
+          if (_DBUS_UNLIKELY (ready_fds[i].flags & _DBUS_WATCH_NVAL))
             {
-              cull_watches_for_invalid_fd (loop, fds[i].fd);
+              cull_watches_for_invalid_fd (loop, ready_fds[i].fd);
               goto next_iteration;
             }
 
-          condition = watch_flags_from_poll_revents (fds[i].revents);
+          condition = ready_fds[i].flags;
+          _dbus_assert ((condition & _DBUS_WATCH_NVAL) == 0);
 
           /* condition may still be 0 if we got some
            * weird POLLFOO thing like POLLWRBAND
@@ -841,11 +798,14 @@ _dbus_loop_iterate (DBusLoop     *loop,
           if (condition == 0)
             continue;
 
-          watches = _dbus_hash_table_lookup_int (loop->watches, fds[i].fd);
+          watches = _dbus_hash_table_lookup_int (loop->watches,
+                                                 ready_fds[i].fd);
 
           if (watches == NULL)
             continue;
 
+          any_oom = FALSE;
+
           for (link = _dbus_list_get_first_link (watches);
               link != NULL;
               link = next)
@@ -863,6 +823,8 @@ _dbus_loop_iterate (DBusLoop     *loop,
                   if (oom)
                     {
                       _dbus_watch_set_oom_last_time (watch, TRUE);
+                      loop->oom_watch_pending = TRUE;
+                      any_oom = TRUE;
                     }
 
 #if MAINLOOP_SPEW
@@ -873,13 +835,19 @@ _dbus_loop_iterate (DBusLoop     *loop,
                   /* We re-check this every time, in case the callback
                    * added/removed watches, which might make our position in
                    * the linked list invalid. See the FIXME above. */
-                  if (initial_serial != loop->callback_list_serial)
-                    goto next_iteration;
+                  if (initial_serial != loop->callback_list_serial ||
+                      loop->depth != orig_depth)
+                    {
+                      if (any_oom)
+                        refresh_watches_for_fd (loop, NULL, ready_fds[i].fd);
 
-                  if (loop->depth != orig_depth)
-                    goto next_iteration;
+                      goto next_iteration;
+                    }
                 }
             }
+
+          if (any_oom)
+            refresh_watches_for_fd (loop, watches, ready_fds[i].fd);
         }
     }
       
@@ -887,9 +855,6 @@ _dbus_loop_iterate (DBusLoop     *loop,
 #if MAINLOOP_SPEW
   _dbus_verbose ("  moving to next iteration\n");
 #endif
-  
-  if (fds && fds != stack_fds)
-    dbus_free (fds);
 
   if (_dbus_loop_dispatch (loop))
     retval = TRUE;
@@ -936,7 +901,7 @@ _dbus_loop_quit (DBusLoop *loop)
 int
 _dbus_get_oom_wait (void)
 {
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
   /* make tests go fast */
   return 0;
 #else