diff options
| author | Havoc Pennington <hp@redhat.com> | 2003-04-07 02:48:14 +0000 | 
|---|---|---|
| committer | Havoc Pennington <hp@redhat.com> | 2003-04-07 02:48:14 +0000 | 
| commit | 4067152efca3d7ae7cd477f4c70d32b8531fecb4 (patch) | |
| tree | 356521e9bc274ab6e030e77b518ecaf75691eea9 | |
| parent | a7f69a41d5b82eb00da20b661181b490039bb70f (diff) | |
commit missing files
| -rw-r--r-- | dbus/dbus-mainloop.c | 715 | ||||
| -rw-r--r-- | dbus/dbus-mainloop.h | 66 | 
2 files changed, 781 insertions, 0 deletions
diff --git a/dbus/dbus-mainloop.c b/dbus/dbus-mainloop.c new file mode 100644 index 00000000..635bd58e --- /dev/null +++ b/dbus/dbus-mainloop.c @@ -0,0 +1,715 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-mainloop.c  Main loop utility + * + * Copyright (C) 2003  Red Hat, Inc. + * + * Licensed under the Academic Free License version 1.2 + *  + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + *  + * 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 + * + */ + +#include "dbus-mainloop.h" + +#include <dbus/dbus-list.h> +#include <dbus/dbus-sysdeps.h> + +struct DBusLoop +{ +  int refcount; +  DBusList *callbacks; +  int callback_list_serial; +  int watch_count; +  int timeout_count; +  int depth; /**< number of recursive runs */ +}; + +typedef enum +{ +  CALLBACK_WATCH, +  CALLBACK_TIMEOUT +} CallbackType; + +typedef struct +{ +  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.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) +{ +  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 (DBusLoop  *loop, +              Callback *cb) +{ +  if (!_dbus_list_append (&loop->callbacks, cb)) +    return FALSE; + +  loop->callback_list_serial += 1; + +  switch (cb->type) +    { +    case CALLBACK_WATCH: +      loop->watch_count += 1; +      break; +    case CALLBACK_TIMEOUT: +      loop->timeout_count += 1; +      break; +    } +   +  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_free (cb); +  _dbus_list_remove_link (&loop->callbacks, link); +  loop->callback_list_serial += 1; +} + +DBusLoop* +_dbus_loop_new (void) +{ +  DBusLoop *loop; + +  loop = dbus_new0 (DBusLoop, 1); +  if (loop == NULL) +    return NULL; + +  loop->refcount = 1; +   +  return loop; +} + +void +_dbus_loop_ref (DBusLoop *loop) +{ +  _dbus_assert (loop != NULL); +  _dbus_assert (loop->refcount > 0); + +  loop->refcount += 1; +} + +void +_dbus_loop_unref (DBusLoop *loop) +{ +  _dbus_assert (loop != NULL); +  _dbus_assert (loop->refcount > 0); + +  loop->refcount -= 1; +  if (loop->refcount == 0) +    { +       +      dbus_free (loop); +    } +} + +dbus_bool_t +_dbus_loop_add_watch (DBusLoop          *loop, +                      DBusWatch        *watch, +                      DBusWatchFunction  function, +                      void             *data, +                      DBusFreeFunction  free_data_func) +{ +  WatchCallback *wcb; + +  wcb = watch_callback_new (watch, function, data, free_data_func); +  if (wcb == NULL) +    return FALSE; + +  if (!add_callback (loop, (Callback*) wcb)) +    { +      wcb->callback.free_data_func = NULL; /* don't want to have this side effect */ +      callback_free ((Callback*) wcb); +      return FALSE; +    } +   +  return TRUE; +} + +void +_dbus_loop_remove_watch (DBusLoop          *loop, +                         DBusWatch        *watch, +                         DBusWatchFunction  function, +                         void             *data) +{ +  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; + +      if (this->type == CALLBACK_WATCH && +          WATCH_CALLBACK (this)->watch == watch && +          this->data == data && +          WATCH_CALLBACK (this)->function == function) +        { +          remove_callback (loop, link); +           +          return; +        } +       +      link = next; +    } + +  _dbus_warn ("could not find watch %p function %p data %p to remove\n", +              watch, function, data); +} + +dbus_bool_t +_dbus_loop_add_timeout (DBusLoop            *loop, +                        DBusTimeout        *timeout, +                        DBusTimeoutFunction  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 (loop, (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 +_dbus_loop_remove_timeout (DBusLoop            *loop, +                           DBusTimeout        *timeout, +                           DBusTimeoutFunction  function, +                           void               *data) +{ +  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; + +      if (this->type == CALLBACK_TIMEOUT && +          TIMEOUT_CALLBACK (this)->timeout == timeout && +          this->data == data && +          TIMEOUT_CALLBACK (this)->function == function) +        { +          remove_callback (loop, link); +           +          return; +        } +       +      link = next; +    } + +  _dbus_warn ("could not find timeout %p function %p data %p to remove\n", +              timeout, function, data); +} + +/* Convolutions from GLib, there really must be a better way + * to do this. + */ +static dbus_bool_t +check_timeout (unsigned long    tv_sec, +               unsigned long    tv_usec, +               TimeoutCallback *tcb, +               int             *timeout) +{ +  long sec; +  long msec; +  unsigned long expiration_tv_sec; +  unsigned long expiration_tv_usec; +  long interval_seconds; +  long interval_milliseconds; +  int interval; + +  interval = dbus_timeout_get_interval (tcb->timeout); +   +  interval_seconds = interval / 1000; +  interval_milliseconds = interval - interval_seconds * 1000; +   +  expiration_tv_sec = tcb->last_tv_sec + interval_seconds; +  expiration_tv_usec = tcb->last_tv_usec + interval_milliseconds * 1000; +  if (expiration_tv_usec >= 1000000) +    { +      expiration_tv_usec -= 1000000; +      expiration_tv_sec += 1; +    } +   +  sec = expiration_tv_sec - tv_sec; +  msec = (expiration_tv_usec - tv_usec) / 1000; + +#if 0 +  printf ("Interval is %ld seconds %ld msecs\n", +          interval_seconds, +          interval_milliseconds); +  printf ("Now is %lu seconds %lu usecs\n", +          tv_sec, tv_usec); +  printf ("Exp is %lu seconds %lu usecs\n", +          expiration_tv_sec, expiration_tv_usec); +  printf ("Pre-correction, remaining sec %ld msec %ld\n", sec, msec); +#endif +   +  /* We do the following in a rather convoluted fashion to deal with +   * the fact that we don't have an integral type big enough to hold +   * the difference of two timevals in millseconds. +   */ +  if (sec < 0 || (sec == 0 && msec < 0)) +    msec = 0; +  else +    { +      if (msec < 0) +	{ +	  msec += 1000; +	  sec -= 1; +	} +       +      if (sec > interval_seconds || +	  (sec == interval_seconds && msec > interval_milliseconds)) +	{ +	  /* The system time has been set backwards, reset the timeout */ +          tcb->last_tv_sec = tv_sec; +          tcb->last_tv_usec = tv_usec; +           +          msec = MIN (_DBUS_INT_MAX, interval); + +          _dbus_verbose ("System clock went backward\n"); +	} +      else +	{ +	  msec = MIN (_DBUS_INT_MAX, (unsigned int)msec + 1000 * (unsigned int)sec); +	} +    } + +  *timeout = msec; + +#if 0 +  printf ("Timeout expires in %d milliseconds\n", *timeout); +#endif +   +  return msec == 0; +} + +/* Returns TRUE if we have any timeouts or ready file descriptors, + * which is just used in test code as a debug hack + */ + +dbus_bool_t +_dbus_loop_iterate (DBusLoop     *loop, +                    dbus_bool_t  block) +{ +  dbus_bool_t retval; +  DBusPollFD *fds; +  int n_fds; +  WatchCallback **watches_for_fds; +  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; +  oom_watch_pending = FALSE; +  orig_depth = loop->depth; +   +#if 0 +  _dbus_verbose (" iterate %d timeouts %d watches\n", +                 loop->timeout_count, loop->watch_count); +#endif +   +  if (loop->callbacks == NULL) +    { +      _dbus_loop_quit (loop); +      goto next_iteration; +    } + +  /* count enabled 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) +        { +          WatchCallback *wcb = WATCH_CALLBACK (cb); + +          if (!wcb->last_iteration_oom && +              dbus_watch_get_enabled (wcb->watch)) +            ++n_fds; +        } +       +      link = next; +    } + +  /* fill our array of fds and watches */ +  if (n_fds > 0) +    { +      fds = dbus_new0 (DBusPollFD, n_fds); +      while (fds == NULL) +        { +          _dbus_wait_for_memory (); +          fds = dbus_new0 (DBusPollFD, n_fds); +        } +           +      watches_for_fds = dbus_new (WatchCallback*, n_fds); +      while (watches_for_fds == NULL) +        { +          _dbus_wait_for_memory (); +          watches_for_fds = dbus_new (WatchCallback*, n_fds); +        } +       +      i = 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; +                } +              else if (dbus_watch_get_enabled (wcb->watch)) +                { +                  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); +    } + +  timeout = -1; +  if (loop->timeout_count > 0) +    { +      unsigned long tv_sec; +      unsigned long tv_usec; + +      retval = TRUE; +       +      _dbus_get_current_time (&tv_sec, &tv_usec); +           +      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_TIMEOUT && +              dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout)) +            { +              TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb); +              int msecs_remaining; + +              check_timeout (tv_sec, tv_usec, tcb, &msecs_remaining); + +              if (timeout < 0) +                timeout = msecs_remaining; +              else +                timeout = MIN (msecs_remaining, timeout); +               +              _dbus_assert (timeout >= 0); +                   +              if (timeout == 0) +                break; /* it's not going to get shorter... */ +            } +               +          link = next; +        } +    } + +  if (!block) +    { +      timeout = 0; +#if 0 +      printf ("timeout is 0 as we aren't blocking\n"); +#endif +    } + +  /* if a watch is OOM, don't wait longer than the OOM +   * wait to re-enable it +   */ +  if (oom_watch_pending) +    timeout = MIN (timeout, _dbus_get_oom_wait ()); +   +  n_ready = _dbus_poll (fds, n_fds, timeout); + +  initial_serial = loop->callback_list_serial; + +  if (loop->timeout_count > 0) +    { +      unsigned long tv_sec; +      unsigned long tv_usec; + +      _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 (&loop->callbacks); +      while (link != NULL) +        { +          DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link); +          Callback *cb = 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)) +            { +              TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb); +              int msecs_remaining; +               +              if (check_timeout (tv_sec, tv_usec, +                                 tcb, &msecs_remaining)) +                { +                  /* Save last callback time and fire this timeout */ +                  tcb->last_tv_sec = tv_sec; +                  tcb->last_tv_usec = tv_usec; + +#if 0 +                  printf ("  invoking timeout\n"); +#endif +                   +                  (* tcb->function) (tcb->timeout, +                                     cb->data); +                } +            } + +          link = next; +        } +    } +       +  if (n_ready > 0) +    { +      i = 0; +      while (i < n_fds) +        { +          /* FIXME I think this "restart if we change the watches" +           * approach could result in starving watches +           * toward the end of the list. +           */ +          if (initial_serial != loop->callback_list_serial) +            goto next_iteration; + +          if (loop->depth != orig_depth) +            goto next_iteration; + +          if (fds[i].revents != 0) +            { +              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)) +                { +                  if (!(* wcb->function) (wcb->watch, +                                          condition, +                                          ((Callback*)wcb)->data)) +                    wcb->last_iteration_oom = TRUE; + +                  retval = TRUE; +                } +            } +               +          ++i; +        } +    } +       + next_iteration: +  dbus_free (fds); +  dbus_free (watches_for_fds); + +  return retval; +} + +void +_dbus_loop_run (DBusLoop *loop) +{ +  int our_exit_depth; + +  _dbus_loop_ref (loop); +   +  our_exit_depth = loop->depth; +  loop->depth += 1; +   +  while (loop->depth != our_exit_depth) +    _dbus_loop_iterate (loop, TRUE); + +  _dbus_loop_unref (loop); +} + +void +_dbus_loop_quit (DBusLoop *loop) +{ +  _dbus_assert (loop->depth > 0); +   +  loop->depth -= 1; +} + +int +_dbus_get_oom_wait (void) +{ +#ifdef DBUS_BUILD_TESTS +  /* make tests go fast */ +  return 0; +#else +  return 500; +#endif +} + +void +_dbus_wait_for_memory (void) +{ +  _dbus_sleep_milliseconds (_dbus_get_oom_wait ()); +} diff --git a/dbus/dbus-mainloop.h b/dbus/dbus-mainloop.h new file mode 100644 index 00000000..bca4317d --- /dev/null +++ b/dbus/dbus-mainloop.h @@ -0,0 +1,66 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-mainloop.h  Main loop utility + * + * Copyright (C) 2003  Red Hat, Inc. + * + * Licensed under the Academic Free License version 1.2 + *  + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + *  + * 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 + * + */ + +#ifndef DBUS_MAINLOOP_H +#define DBUS_MAINLOOP_H + +#include <dbus/dbus.h> + +typedef struct DBusLoop DBusLoop; + +typedef dbus_bool_t (* DBusWatchFunction)   (DBusWatch     *watch, +                                             unsigned int   condition, +                                             void          *data); +typedef void        (* DBusTimeoutFunction) (DBusTimeout   *timeout, +                                             void          *data); + +DBusLoop*   _dbus_loop_new            (void); +void        _dbus_loop_ref            (DBusLoop            *loop); +void        _dbus_loop_unref          (DBusLoop            *loop); +dbus_bool_t _dbus_loop_add_watch      (DBusLoop            *loop, +                                       DBusWatch           *watch, +                                       DBusWatchFunction    function, +                                       void                *data, +                                       DBusFreeFunction     free_data_func); +void        _dbus_loop_remove_watch   (DBusLoop            *loop, +                                       DBusWatch           *watch, +                                       DBusWatchFunction    function, +                                       void                *data); +dbus_bool_t _dbus_loop_add_timeout    (DBusLoop            *loop, +                                       DBusTimeout         *timeout, +                                       DBusTimeoutFunction  function, +                                       void                *data, +                                       DBusFreeFunction     free_data_func); +void        _dbus_loop_remove_timeout (DBusLoop            *loop, +                                       DBusTimeout         *timeout, +                                       DBusTimeoutFunction  function, +                                       void                *data); +void        _dbus_loop_run            (DBusLoop            *loop); +void        _dbus_loop_quit           (DBusLoop            *loop); +dbus_bool_t _dbus_loop_iterate        (DBusLoop            *loop, +                                       dbus_bool_t          block); + +int  _dbus_get_oom_wait    (void); +void _dbus_wait_for_memory (void); + +#endif /* DBUS_MAINLOOP_H */  | 
