diff options
Diffstat (limited to 'bus/loop.c')
-rw-r--r-- | bus/loop.c | 415 |
1 files changed, 352 insertions, 63 deletions
@@ -26,20 +26,46 @@ #include <dbus/dbus-list.h> #include <dbus/dbus-sysdeps.h> -static DBusList *watches = NULL; -static int watch_list_serial = 0; +static DBusList *callbacks = NULL; +static int callback_list_serial = 0; +static int watch_count = 0; +static int timeout_count = 0; static dbus_bool_t exited = FALSE; +typedef enum +{ + CALLBACK_WATCH, + CALLBACK_TIMEOUT +} CallbackType; + typedef struct { - DBusWatch *watch; - BusWatchFunction function; + CallbackType type; void *data; DBusFreeFunction free_data_func; +} Callback; + +typedef struct +{ + Callback callback; + BusWatchFunction function; + DBusWatch *watch; } WatchCallback; -dbus_bool_t -bus_loop_add_watch (DBusWatch *watch, +typedef struct +{ + Callback callback; + DBusTimeout *timeout; + BusTimeoutFunction 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, BusWatchFunction function, void *data, DBusFreeFunction free_data_func) @@ -48,20 +74,108 @@ bus_loop_add_watch (DBusWatch *watch, cb = dbus_new (WatchCallback, 1); if (cb == NULL) - return FALSE; + return NULL; cb->watch = watch; cb->function = function; - cb->data = data; - cb->free_data_func = free_data_func; + cb->callback.type = CALLBACK_WATCH; + cb->callback.data = data; + cb->callback.free_data_func = free_data_func; + + return cb; +} - if (!_dbus_list_append (&watches, cb)) +static TimeoutCallback* +timeout_callback_new (DBusTimeout *timeout, + BusTimeoutFunction function, + void *data, + DBusFreeFunction free_data_func) +{ + TimeoutCallback *cb; + + cb = dbus_new (TimeoutCallback, 1); + if (cb == NULL) + return NULL; + + cb->timeout = timeout; + cb->function = function; + _dbus_get_current_time (&cb->last_tv_sec, + &cb->last_tv_usec); + cb->callback.type = CALLBACK_TIMEOUT; + cb->callback.data = data; + cb->callback.free_data_func = free_data_func; + + return cb; +} + +static void +callback_free (Callback *cb) +{ + if (cb->free_data_func) + (* cb->free_data_func) (cb->data); + + dbus_free (cb); +} + +static dbus_bool_t +add_callback (Callback *cb) +{ + if (!_dbus_list_append (&callbacks, cb)) + return FALSE; + + callback_list_serial += 1; + + switch (cb->type) { - dbus_free (cb); - return FALSE; + case CALLBACK_WATCH: + watch_count += 1; + break; + case CALLBACK_TIMEOUT: + timeout_count += 1; + break; } + + return TRUE; +} + +static void +remove_callback (DBusList *link) +{ + Callback *cb = link->data; + + switch (cb->type) + { + case CALLBACK_WATCH: + watch_count -= 1; + break; + case CALLBACK_TIMEOUT: + timeout_count -= 1; + break; + } + + callback_free (cb); + _dbus_list_remove_link (&callbacks, link); + callback_list_serial += 1; +} + +dbus_bool_t +bus_loop_add_watch (DBusWatch *watch, + BusWatchFunction function, + void *data, + DBusFreeFunction free_data_func) +{ + WatchCallback *wcb; + + wcb = watch_callback_new (watch, function, data, free_data_func); + if (wcb == NULL) + return FALSE; - watch_list_serial += 1; + if (!add_callback ((Callback*) wcb)) + { + wcb->callback.free_data_func = NULL; /* don't want to have this side effect */ + callback_free ((Callback*) wcb); + return FALSE; + } return TRUE; } @@ -73,23 +187,18 @@ bus_loop_remove_watch (DBusWatch *watch, { DBusList *link; - link = _dbus_list_get_first_link (&watches); + link = _dbus_list_get_first_link (&callbacks); while (link != NULL) { - DBusList *next = _dbus_list_get_next_link (&watches, link); - WatchCallback *cb = link->data; + DBusList *next = _dbus_list_get_next_link (&callbacks, link); + Callback *this = link->data; - if (cb->watch == watch && - cb->function == function && - cb->data == data) + if (this->type == CALLBACK_WATCH && + WATCH_CALLBACK (this)->watch == watch && + this->data == data && + WATCH_CALLBACK (this)->function == function) { - _dbus_list_remove_link (&watches, link); - - watch_list_serial += 1; - - if (cb->free_data_func) - (* cb->free_data_func) (cb->data); - dbus_free (cb); + remove_callback (link); return; } @@ -101,6 +210,58 @@ bus_loop_remove_watch (DBusWatch *watch, watch, function, data); } +dbus_bool_t +bus_loop_add_timeout (DBusTimeout *timeout, + BusTimeoutFunction function, + void *data, + DBusFreeFunction free_data_func) +{ + TimeoutCallback *tcb; + + tcb = timeout_callback_new (timeout, function, data, free_data_func); + if (tcb == NULL) + return FALSE; + + if (!add_callback ((Callback*) tcb)) + { + tcb->callback.free_data_func = NULL; /* don't want to have this side effect */ + callback_free ((Callback*) tcb); + return FALSE; + } + + return TRUE; +} + +void +bus_loop_remove_timeout (DBusTimeout *timeout, + BusTimeoutFunction function, + void *data) +{ + DBusList *link; + + link = _dbus_list_get_first_link (&callbacks); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&callbacks, link); + Callback *this = link->data; + + if (this->type == CALLBACK_TIMEOUT && + TIMEOUT_CALLBACK (this)->timeout == timeout && + this->data == data && + TIMEOUT_CALLBACK (this)->function == function) + { + remove_callback (link); + + return; + } + + link = next; + } + + _dbus_warn ("could not find timeout %p function %p data %p to remove\n", + timeout, function, data); +} + void bus_loop_run (void) { @@ -113,59 +274,187 @@ bus_loop_run (void) DBusList *link; int n_ready; int initial_serial; + long timeout; fds = NULL; watches_for_fds = NULL; - n_fds = _dbus_list_get_length (&watches); - - if (n_fds == 0) + if (callbacks == NULL) { bus_loop_quit (); goto next_iteration; } - fds = dbus_new0 (DBusPollFD, n_fds); - while (fds == NULL) + n_fds = watch_count; + + if (n_fds > 0) { - bus_wait_for_memory (); fds = dbus_new0 (DBusPollFD, n_fds); + while (fds == NULL) + { + bus_wait_for_memory (); + fds = dbus_new0 (DBusPollFD, n_fds); + } + + watches_for_fds = dbus_new (WatchCallback*, n_fds); + while (watches_for_fds == NULL) + { + bus_wait_for_memory (); + watches_for_fds = dbus_new (WatchCallback*, n_fds); + } + + i = 0; + link = _dbus_list_get_first_link (&callbacks); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&callbacks, link); + Callback *cb = link->data; + if (cb->type == CALLBACK_WATCH) + { + unsigned int flags; + WatchCallback *wcb = WATCH_CALLBACK (cb); + + watches_for_fds[i] = wcb; + + flags = dbus_watch_get_flags (wcb->watch); + + fds[i].fd = dbus_watch_get_fd (wcb->watch); + if (flags & DBUS_WATCH_READABLE) + fds[i].events |= _DBUS_POLLIN; + if (flags & DBUS_WATCH_WRITABLE) + fds[i].events |= _DBUS_POLLOUT; + + ++i; + } + + link = next; + } + + _dbus_assert (i == n_fds); } - watches_for_fds = dbus_new (WatchCallback*, n_fds); - while (watches_for_fds == NULL) + timeout = -1; + if (timeout_count > 0) { - bus_wait_for_memory (); - watches_for_fds = dbus_new (WatchCallback*, n_fds); + unsigned long tv_sec; + unsigned long tv_usec; + + _dbus_get_current_time (&tv_sec, &tv_usec); + + link = _dbus_list_get_first_link (&callbacks); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&callbacks, link); + Callback *cb = link->data; + + if (cb->type == CALLBACK_TIMEOUT) + { + TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb); + unsigned long interval; + unsigned long elapsed; + + if (tcb->last_tv_sec > tv_sec || + (tcb->last_tv_sec == tv_sec && + tcb->last_tv_usec > tv_usec)) + { + /* Clock went backward, pretend timeout + * was just installed. + */ + tcb->last_tv_sec = tv_sec; + tcb->last_tv_usec = tv_usec; + _dbus_verbose ("System clock went backward\n"); + } + + interval = dbus_timeout_get_interval (tcb->timeout); + + elapsed = + (tv_sec - tcb->last_tv_sec) * 1000 + + (tv_usec - tcb->last_tv_usec) / 1000; + + if (interval < elapsed) + timeout = 0; + else if (timeout < 0) + timeout = interval - elapsed; + else + timeout = MIN (((unsigned long)timeout), interval - elapsed); + + _dbus_assert (timeout >= 0); + + if (timeout == 0) + break; /* it's not going to get shorter... */ + } + + link = next; + } } - i = 0; - link = _dbus_list_get_first_link (&watches); - while (link != NULL) + n_ready = _dbus_poll (fds, n_fds, timeout); + + initial_serial = callback_list_serial; + + if (timeout_count > 0) { - DBusList *next = _dbus_list_get_next_link (&watches, link); - WatchCallback *cb = link->data; - int flags; - - watches_for_fds[i] = cb; + unsigned long tv_sec; + unsigned long tv_usec; - flags = dbus_watch_get_flags (cb->watch); - - fds[i].fd = dbus_watch_get_fd (cb->watch); - if (flags & DBUS_WATCH_READABLE) - fds[i].events |= _DBUS_POLLIN; - if (flags & DBUS_WATCH_WRITABLE) - fds[i].events |= _DBUS_POLLOUT; - - link = next; - ++i; - } + _dbus_get_current_time (&tv_sec, &tv_usec); + + /* It'd be nice to avoid this O(n) thingy here */ + link = _dbus_list_get_first_link (&callbacks); + while (link != NULL) + { + DBusList *next = _dbus_list_get_next_link (&callbacks, link); + Callback *cb = link->data; + + if (initial_serial != callback_list_serial) + goto next_iteration; + + if (exited) + goto next_iteration; + + if (cb->type == CALLBACK_TIMEOUT) + { + TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb); + unsigned long interval; + unsigned long elapsed; + + if (tcb->last_tv_sec > tv_sec || + (tcb->last_tv_sec == tv_sec && + tcb->last_tv_usec > tv_usec)) + { + /* Clock went backward, pretend timeout + * was just installed. + */ + tcb->last_tv_sec = tv_sec; + tcb->last_tv_usec = tv_usec; + _dbus_verbose ("System clock went backward\n"); + goto next_timeout; + } + + interval = dbus_timeout_get_interval (tcb->timeout); - n_ready = _dbus_poll (fds, n_fds, -1); + elapsed = + (tv_sec - tcb->last_tv_sec) * 1000 + + (tv_usec - tcb->last_tv_usec) / 1000; + if (interval <= elapsed) + { + /* Save last callback time and fire this timeout */ + tcb->last_tv_sec = tv_sec; + tcb->last_tv_usec = tv_usec; + + (* tcb->function) (tcb->timeout, + cb->data); + } + } + + next_timeout: + link = next; + } + } + if (n_ready > 0) { - initial_serial = watch_list_serial; i = 0; while (i < n_fds) { @@ -173,7 +462,7 @@ bus_loop_run (void) * approach could result in starving watches * toward the end of the list. */ - if (initial_serial != watch_list_serial) + if (initial_serial != callback_list_serial) goto next_iteration; if (exited) @@ -181,10 +470,10 @@ bus_loop_run (void) if (fds[i].revents != 0) { - WatchCallback *cb; + WatchCallback *wcb; unsigned int condition; - cb = watches_for_fds[i]; + wcb = watches_for_fds[i]; condition = 0; if (fds[i].revents & _DBUS_POLLIN) @@ -201,9 +490,9 @@ bus_loop_run (void) */ if (condition != 0) - (* cb->function) (cb->watch, - condition, - cb->data); + (* wcb->function) (wcb->watch, + condition, + ((Callback*)wcb)->data); } ++i; |