X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dbus%2Fdbus-mainloop.c;h=8b4da164969496819157ec8d375ac4c98a24528d;hb=383f596c4aee2561c90abca3ce9d1f52407a3eec;hp=ab595af475dca66089f6490310a44d92709a71d7;hpb=d012387afef0ba02185ebe27bc6bb15551912e92;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-mainloop.c b/dbus/dbus-mainloop.c index ab595af..8b4da16 100644 --- a/dbus/dbus-mainloop.c +++ b/dbus/dbus-mainloop.c @@ -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 * @@ -17,114 +18,50 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ +#include #include "dbus-mainloop.h" #ifndef DOXYGEN_SHOULD_SKIP_THIS +#include #include -#include +#include +#include #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; - DBusList *callbacks; + /** 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; }; -typedef enum -{ - CALLBACK_WATCH, - CALLBACK_TIMEOUT -} CallbackType; - typedef struct { - int refcount; - CallbackType type; - void *data; - DBusFreeFunction free_data_func; -} Callback; - -typedef struct -{ - Callback callback; - DBusWatchFunction function; - DBusWatch *watch; - /* last watch handle failed due to OOM */ - unsigned int last_iteration_oom : 1; -} WatchCallback; - -typedef struct -{ - Callback callback; DBusTimeout *timeout; - DBusTimeoutFunction function; unsigned long last_tv_sec; unsigned long last_tv_usec; } TimeoutCallback; -#define WATCH_CALLBACK(callback) ((WatchCallback*)callback) #define TIMEOUT_CALLBACK(callback) ((TimeoutCallback*)callback) -static WatchCallback* -watch_callback_new (DBusWatch *watch, - DBusWatchFunction function, - void *data, - DBusFreeFunction free_data_func) -{ - WatchCallback *cb; - - cb = dbus_new (WatchCallback, 1); - if (cb == NULL) - return NULL; - - cb->watch = watch; - cb->function = function; - cb->last_iteration_oom = FALSE; - cb->callback.refcount = 1; - cb->callback.type = CALLBACK_WATCH; - cb->callback.data = data; - cb->callback.free_data_func = free_data_func; - - return cb; -} - static TimeoutCallback* -timeout_callback_new (DBusTimeout *timeout, - DBusTimeoutFunction function, - void *data, - DBusFreeFunction free_data_func) +timeout_callback_new (DBusTimeout *timeout) { TimeoutCallback *cb; @@ -133,84 +70,37 @@ timeout_callback_new (DBusTimeout *timeout, return NULL; cb->timeout = timeout; - cb->function = function; - _dbus_get_current_time (&cb->last_tv_sec, - &cb->last_tv_usec); - cb->callback.refcount = 1; - cb->callback.type = CALLBACK_TIMEOUT; - cb->callback.data = data; - cb->callback.free_data_func = free_data_func; - - return cb; -} - -static Callback * -callback_ref (Callback *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 -callback_unref (Callback *cb) +timeout_callback_free (TimeoutCallback *cb) { - _dbus_assert (cb->refcount > 0); - - cb->refcount -= 1; - - if (cb->refcount == 0) - { - if (cb->free_data_func) - (* cb->free_data_func) (cb->data); - - dbus_free (cb); - } + dbus_free (cb); } -static dbus_bool_t -add_callback (DBusLoop *loop, - Callback *cb) +static void +free_watch_table_entry (void *data) { - if (!_dbus_list_append (&loop->callbacks, cb)) - return FALSE; + DBusList **watches = data; + DBusWatch *watch; - loop->callback_list_serial += 1; + /* DBusHashTable sometimes calls free_function(NULL) even if you never + * have NULL as a value */ + if (watches == NULL) + return; - switch (cb->type) + for (watch = _dbus_list_pop_first (watches); + watch != NULL; + watch = _dbus_list_pop_first (watches)) { - case CALLBACK_WATCH: - loop->watch_count += 1; - break; - case CALLBACK_TIMEOUT: - loop->timeout_count += 1; - break; + _dbus_watch_unref (watch); } - - return TRUE; -} -static void -remove_callback (DBusLoop *loop, - DBusList *link) -{ - Callback *cb = link->data; - - switch (cb->type) - { - case CALLBACK_WATCH: - loop->watch_count -= 1; - break; - case CALLBACK_TIMEOUT: - loop->timeout_count -= 1; - break; - } - - callback_unref (cb); - _dbus_list_remove_link (&loop->callbacks, link); - loop->callback_list_serial += 1; + _dbus_assert (*watches == NULL); + dbus_free (watches); } DBusLoop* @@ -222,8 +112,25 @@ _dbus_loop_new (void) if (loop == NULL) return NULL; + loop->watches = _dbus_hash_table_new (DBUS_HASH_INT, NULL, + free_watch_table_entry); + + 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; + } + loop->refcount = 1; - + return loop; } @@ -253,82 +160,229 @@ _dbus_loop_unref (DBusLoop *loop) dbus_connection_unref (connection); } - + + _dbus_hash_table_unref (loop->watches); + _dbus_socket_set_free (loop->socket_set); dbus_free (loop); } } +static DBusList ** +ensure_watch_table_entry (DBusLoop *loop, + int fd) +{ + DBusList **watches; + + watches = _dbus_hash_table_lookup_int (loop->watches, fd); + + if (watches == NULL) + { + watches = dbus_new0 (DBusList *, 1); + + if (watches == NULL) + return watches; + + if (!_dbus_hash_table_insert_int (loop->watches, fd, watches)) + { + dbus_free (watches); + watches = NULL; + } + } + + return watches; +} + +static void +cull_watches_for_invalid_fd (DBusLoop *loop, + int fd) +{ + DBusList *link; + DBusList **watches; + + _dbus_warn ("invalid request, socket fd %d not open\n", fd); + watches = _dbus_hash_table_lookup_int (loop->watches, fd); + + if (watches != NULL) + { + for (link = _dbus_list_get_first_link (watches); + link != NULL; + link = _dbus_list_get_next_link (watches, link)) + _dbus_watch_invalidate (link->data); + } + + _dbus_hash_table_remove_int (loop->watches, fd); +} + +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 FALSE; + + /* We can't GC hash table entries if they're non-empty lists */ + if (*watches != NULL) + 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 -_dbus_loop_add_watch (DBusLoop *loop, - DBusWatch *watch, - DBusWatchFunction function, - void *data, - DBusFreeFunction free_data_func) +_dbus_loop_add_watch (DBusLoop *loop, + DBusWatch *watch) { - WatchCallback *wcb; + int fd; + DBusList **watches; + + fd = dbus_watch_get_socket (watch); + _dbus_assert (fd != -1); - wcb = watch_callback_new (watch, function, data, free_data_func); - if (wcb == NULL) + watches = ensure_watch_table_entry (loop, fd); + + if (watches == NULL) return FALSE; - if (!add_callback (loop, (Callback*) wcb)) + if (!_dbus_list_append (watches, _dbus_watch_ref (watch))) { - wcb->callback.free_data_func = NULL; /* don't want to have this side effect */ - callback_unref ((Callback*) wcb); + _dbus_watch_unref (watch); + gc_watch_table_entry (loop, watches, fd); + 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_remove_watch (DBusLoop *loop, - DBusWatch *watch, - DBusWatchFunction function, - void *data) +_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) { + DBusList **watches; DBusList *link; - - link = _dbus_list_get_first_link (&loop->callbacks); - while (link != NULL) - { - DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link); - Callback *this = link->data; + int fd; + + /* This relies on people removing watches before they invalidate them, + * which has been safe since fd.o #33336 was fixed. Assert about it + * so we don't regress. */ + fd = dbus_watch_get_socket (watch); + _dbus_assert (fd != -1); - if (this->type == CALLBACK_WATCH && - WATCH_CALLBACK (this)->watch == watch && - this->data == data && - WATCH_CALLBACK (this)->function == function) + watches = _dbus_hash_table_lookup_int (loop->watches, fd); + + if (watches != NULL) + { + link = _dbus_list_get_first_link (watches); + while (link != NULL) { - remove_callback (loop, link); - - return; - } - - link = next; - } + DBusList *next = _dbus_list_get_next_link (watches, link); + DBusWatch *this = link->data; + + if (this == watch) + { + _dbus_list_remove_link (watches, link); + loop->callback_list_serial += 1; + loop->watch_count -= 1; + _dbus_watch_unref (this); + + /* if that was the last watch for that fd, drop the hash table + * 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); + } - _dbus_warn ("could not find watch %p function %p data %p to remove\n", - watch, (void *)function, data); + return; + } + + link = next; + } + } + + _dbus_warn ("could not find watch %p to remove\n", watch); } dbus_bool_t -_dbus_loop_add_timeout (DBusLoop *loop, - DBusTimeout *timeout, - DBusTimeoutFunction function, - void *data, - DBusFreeFunction free_data_func) +_dbus_loop_add_timeout (DBusLoop *loop, + DBusTimeout *timeout) { TimeoutCallback *tcb; - tcb = timeout_callback_new (timeout, function, data, free_data_func); + tcb = timeout_callback_new (timeout); if (tcb == NULL) return FALSE; - if (!add_callback (loop, (Callback*) tcb)) + if (_dbus_list_append (&loop->timeouts, tcb)) + { + loop->callback_list_serial += 1; + loop->timeout_count += 1; + } + else { - tcb->callback.free_data_func = NULL; /* don't want to have this side effect */ - callback_unref ((Callback*) tcb); + timeout_callback_free (tcb); return FALSE; } @@ -336,34 +390,31 @@ _dbus_loop_add_timeout (DBusLoop *loop, } void -_dbus_loop_remove_timeout (DBusLoop *loop, - DBusTimeout *timeout, - DBusTimeoutFunction function, - void *data) +_dbus_loop_remove_timeout (DBusLoop *loop, + DBusTimeout *timeout) { DBusList *link; - link = _dbus_list_get_first_link (&loop->callbacks); + link = _dbus_list_get_first_link (&loop->timeouts); while (link != NULL) { - DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link); - Callback *this = link->data; + DBusList *next = _dbus_list_get_next_link (&loop->timeouts, link); + TimeoutCallback *this = link->data; - if (this->type == CALLBACK_TIMEOUT && - TIMEOUT_CALLBACK (this)->timeout == timeout && - this->data == data && - TIMEOUT_CALLBACK (this)->function == function) + if (this->timeout == timeout) { - remove_callback (loop, link); - + _dbus_list_remove_link (&loop->timeouts, link); + loop->callback_list_serial += 1; + loop->timeout_count -= 1; + timeout_callback_free (this); + return; } link = next; } - _dbus_warn ("could not find timeout %p function %p data %p to remove\n", - timeout, (void *)function, data); + _dbus_warn ("could not find timeout %p to remove\n", timeout); } /* Convolutions from GLib, there really must be a better way @@ -520,141 +571,43 @@ _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; - WatchCallback **watches_for_fds; - WatchCallback *stack_watches_for_fds[N_STACK_DESCRIPTORS]; + 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; - + retval = FALSE; - fds = NULL; - watches_for_fds = NULL; - n_fds = 0; - oom_watch_pending = FALSE; orig_depth = loop->depth; #if MAINLOOP_SPEW _dbus_verbose ("Iteration block=%d depth=%d timeout_count=%d watch_count=%d\n", block, loop->depth, loop->timeout_count, loop->watch_count); #endif - - if (loop->callbacks == 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); - } - - watches_for_fds = dbus_new (WatchCallback*, loop->watch_count); - while (watches_for_fds == NULL) - { - _dbus_wait_for_memory (); - watches_for_fds = dbus_new (WatchCallback*, loop->watch_count); - } - } - else - { - fds = stack_fds; - watches_for_fds = stack_watches_for_fds; - } - - /* fill our array of fds and watches */ - n_fds = 0; - link = _dbus_list_get_first_link (&loop->callbacks); - while (link != NULL) - { - DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link); - Callback *cb = link->data; - if (cb->type == CALLBACK_WATCH) - { - unsigned int flags; - WatchCallback *wcb = WATCH_CALLBACK (cb); - - if (wcb->last_iteration_oom) - { - /* we skip this one this time, but reenable it next time, - * and have a timeout on this iteration - */ - wcb->last_iteration_oom = 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", - dbus_watch_get_socket (wcb->watch)); -#endif - } - else if (dbus_watch_get_enabled (wcb->watch)) - { - watches_for_fds[n_fds] = wcb; - - callback_ref (cb); - - flags = dbus_watch_get_flags (wcb->watch); - - fds[n_fds].fd = dbus_watch_get_socket (wcb->watch); - fds[n_fds].revents = 0; - fds[n_fds].events = 0; - if (flags & DBUS_WATCH_READABLE) - fds[n_fds].events |= _DBUS_POLLIN; - if (flags & DBUS_WATCH_WRITABLE) - fds[n_fds].events |= _DBUS_POLLOUT; - -#if MAINLOOP_SPEW - _dbus_verbose (" polling watch on fd %d %s\n", - fds[n_fds].fd, watch_flags_to_string (flags)); -#endif + if (_dbus_hash_table_get_n_entries (loop->watches) == 0 && + loop->timeouts == NULL) + goto next_iteration; - n_fds += 1; - } - else - { -#if MAINLOOP_SPEW - _dbus_verbose (" skipping disabled watch on fd %d %s\n", - dbus_watch_get_socket (wcb->watch), - watch_flags_to_string (dbus_watch_get_flags (wcb->watch))); -#endif - } - } - - link = next; - } - timeout = -1; if (loop->timeout_count > 0) { unsigned long tv_sec; unsigned long tv_usec; - _dbus_get_current_time (&tv_sec, &tv_usec); - - link = _dbus_list_get_first_link (&loop->callbacks); + _dbus_get_monotonic_time (&tv_sec, &tv_usec); + + link = _dbus_list_get_first_link (&loop->timeouts); while (link != NULL) { - DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link); - Callback *cb = link->data; + DBusList *next = _dbus_list_get_next_link (&loop->timeouts, link); + TimeoutCallback *tcb = link->data; - if (cb->type == CALLBACK_TIMEOUT && - dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout)) + if (dbus_timeout_get_enabled (tcb->timeout)) { - TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb); int msecs_remaining; check_timeout (tv_sec, tv_usec, tcb, &msecs_remaining); @@ -675,7 +628,7 @@ _dbus_loop_iterate (DBusLoop *loop, break; /* it's not going to get shorter... */ } #if MAINLOOP_SPEW - else if (cb->type == CALLBACK_TIMEOUT) + else { _dbus_verbose (" skipping disabled timeout\n"); } @@ -694,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; @@ -713,25 +707,23 @@ _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->callbacks); + link = _dbus_list_get_first_link (&loop->timeouts); while (link != NULL) { - DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link); - Callback *cb = link->data; + DBusList *next = _dbus_list_get_next_link (&loop->timeouts, link); + TimeoutCallback *tcb = link->data; if (initial_serial != loop->callback_list_serial) goto next_iteration; if (loop->depth != orig_depth) goto next_iteration; - - if (cb->type == CALLBACK_TIMEOUT && - dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout)) + + if (dbus_timeout_get_enabled (tcb->timeout)) { - TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb); int msecs_remaining; if (check_timeout (tv_sec, tv_usec, @@ -744,9 +736,11 @@ _dbus_loop_iterate (DBusLoop *loop, #if MAINLOOP_SPEW _dbus_verbose (" invoking timeout\n"); #endif - - (* tcb->function) (tcb->timeout, - cb->data); + + /* can theoretically return FALSE on OOM, but we just + * let it fire again later - in practice that's what + * every wrapper callback in dbus-daemon used to do */ + dbus_timeout_handle (tcb->timeout); retval = TRUE; } @@ -758,7 +752,7 @@ _dbus_loop_iterate (DBusLoop *loop, } } #if MAINLOOP_SPEW - else if (cb->type == CALLBACK_TIMEOUT) + else { _dbus_verbose (" skipping invocation of disabled timeout\n"); } @@ -767,12 +761,16 @@ _dbus_loop_iterate (DBusLoop *loop, link = next; } } - + if (n_ready > 0) { - i = 0; - while (i < n_fds) + 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 * toward the end of the list. @@ -783,45 +781,73 @@ _dbus_loop_iterate (DBusLoop *loop, if (loop->depth != orig_depth) goto next_iteration; - if (fds[i].revents != 0) + _dbus_assert (ready_fds[i].flags != 0); + + if (_DBUS_UNLIKELY (ready_fds[i].flags & _DBUS_WATCH_NVAL)) { - WatchCallback *wcb; - unsigned int condition; - - wcb = watches_for_fds[i]; - - condition = 0; - if (fds[i].revents & _DBUS_POLLIN) - condition |= DBUS_WATCH_READABLE; - if (fds[i].revents & _DBUS_POLLOUT) - condition |= DBUS_WATCH_WRITABLE; - if (fds[i].revents & _DBUS_POLLHUP) - condition |= DBUS_WATCH_HANGUP; - if (fds[i].revents & _DBUS_POLLERR) - condition |= DBUS_WATCH_ERROR; - - /* condition may still be 0 if we got some - * weird POLLFOO thing like POLLWRBAND - */ - - if (condition != 0 && - dbus_watch_get_enabled (wcb->watch)) + cull_watches_for_invalid_fd (loop, ready_fds[i].fd); + goto next_iteration; + } + + 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 + */ + if (condition == 0) + continue; + + 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) + { + DBusWatch *watch = link->data; + + next = _dbus_list_get_next_link (watches, link); + + if (dbus_watch_get_enabled (watch)) { - if (!(* wcb->function) (wcb->watch, - condition, - ((Callback*)wcb)->data)) - wcb->last_iteration_oom = TRUE; + dbus_bool_t oom; + + oom = !dbus_watch_handle (watch, condition); + + if (oom) + { + _dbus_watch_set_oom_last_time (watch, TRUE); + loop->oom_watch_pending = TRUE; + any_oom = TRUE; + } #if MAINLOOP_SPEW - _dbus_verbose (" Invoked watch, oom = %d\n", - wcb->last_iteration_oom); + _dbus_verbose (" Invoked watch, oom = %d\n", oom); #endif - retval = TRUE; + + /* 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 || + loop->depth != orig_depth) + { + if (any_oom) + refresh_watches_for_fd (loop, NULL, ready_fds[i].fd); + + goto next_iteration; + } } } - - ++i; + + if (any_oom) + refresh_watches_for_fd (loop, watches, ready_fds[i].fd); } } @@ -829,22 +855,7 @@ _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 (watches_for_fds) - { - i = 0; - while (i < n_fds) - { - callback_unref (&watches_for_fds[i]->callback); - ++i; - } - - if (watches_for_fds != stack_watches_for_fds) - dbus_free (watches_for_fds); - } - + if (_dbus_loop_dispatch (loop)) retval = TRUE; @@ -890,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