ofdbus/dbus/bus/expirelist.c
changeset 0 e4d67989cc36
equal deleted inserted replaced
-1:000000000000 0:e4d67989cc36
       
     1 /* -*- mode: C; c-file-style: "gnu" -*- */
       
     2 /* expirelist.c  List of items that expire
       
     3  *
       
     4  * Copyright (C) 2003  Red Hat, Inc.
       
     5  * Portion Copyright © 2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
       
     6  * Licensed under the Academic Free License version 2.1
       
     7  *
       
     8  * This program is free software; you can redistribute it and/or modify
       
     9  * it under the terms of the GNU General Public License as published by
       
    10  * the Free Software Foundation; either version 2 of the License, or
       
    11  * (at your option) any later version.
       
    12  *
       
    13  * This program is distributed in the hope that it will be useful,
       
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    16  * GNU General Public License for more details.
       
    17  *
       
    18  * You should have received a copy of the GNU General Public License
       
    19  * along with this program; if not, write to the Free Software
       
    20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    21  *
       
    22  */
       
    23 
       
    24 #include "expirelist.h"
       
    25 #include "test.h"
       
    26 #ifndef __SYMBIAN32__
       
    27 #include <dbus/dbus-internals.h>
       
    28 #include <dbus/dbus-mainloop.h>
       
    29 #include <dbus/dbus-timeout.h>
       
    30 #else
       
    31 #include "dbus-internals.h"
       
    32 #include "dbus-mainloop.h"
       
    33 #include "dbus-timeout.h"
       
    34 #endif //__SYMBIAN32__
       
    35 
       
    36 static dbus_bool_t expire_timeout_handler (void *data);
       
    37 
       
    38 static void
       
    39 call_timeout_callback (DBusTimeout   *timeout,
       
    40                        void          *data)
       
    41 {
       
    42   /* can return FALSE on OOM but we just let it fire again later */
       
    43   dbus_timeout_handle (timeout);
       
    44 }
       
    45 
       
    46 BusExpireList*
       
    47 bus_expire_list_new (DBusLoop      *loop,
       
    48                      int            expire_after,
       
    49                      BusExpireFunc  expire_func,
       
    50                      void          *data)
       
    51 {
       
    52   BusExpireList *list;
       
    53 
       
    54   list = dbus_new0 (BusExpireList, 1);
       
    55   if (list == NULL)
       
    56     return NULL;
       
    57 
       
    58   list->expire_func = expire_func;
       
    59   list->data = data;
       
    60   list->loop = loop;
       
    61   list->expire_after = expire_after;
       
    62 
       
    63   list->timeout = _dbus_timeout_new (100, /* irrelevant */
       
    64                                      expire_timeout_handler,
       
    65                                      list, NULL);
       
    66   if (list->timeout == NULL)
       
    67     goto failed;
       
    68 
       
    69   _dbus_timeout_set_enabled (list->timeout, FALSE);
       
    70 
       
    71   if (!_dbus_loop_add_timeout (list->loop,
       
    72                                list->timeout,
       
    73                                call_timeout_callback, NULL, NULL))
       
    74     goto failed;
       
    75 
       
    76   return list;
       
    77 
       
    78  failed:
       
    79   if (list->timeout)
       
    80     _dbus_timeout_unref (list->timeout);
       
    81 
       
    82   dbus_free (list);
       
    83 
       
    84   return NULL;
       
    85 }
       
    86 
       
    87 void
       
    88 bus_expire_list_free (BusExpireList *list)
       
    89 {
       
    90   _dbus_assert (list->items == NULL);
       
    91 
       
    92   _dbus_loop_remove_timeout (list->loop, list->timeout,
       
    93                              call_timeout_callback, NULL);
       
    94 
       
    95   _dbus_timeout_unref (list->timeout);
       
    96 
       
    97   dbus_free (list);
       
    98 }
       
    99 
       
   100 void
       
   101 bus_expire_timeout_set_interval (DBusTimeout *timeout,
       
   102                                  int          next_interval)
       
   103 {
       
   104   if (next_interval >= 0)
       
   105     {
       
   106       _dbus_timeout_set_interval (timeout,
       
   107                                   next_interval);
       
   108       _dbus_timeout_set_enabled (timeout, TRUE);
       
   109 
       
   110       _dbus_verbose ("Enabled expire timeout with interval %d\n",
       
   111                      next_interval);
       
   112     }
       
   113   else if (dbus_timeout_get_enabled (timeout))
       
   114     {
       
   115       _dbus_timeout_set_enabled (timeout, FALSE);
       
   116 
       
   117       _dbus_verbose ("Disabled expire timeout\n");
       
   118     }
       
   119   else
       
   120     _dbus_verbose ("No need to disable expire timeout\n");
       
   121 }
       
   122 
       
   123 static int
       
   124 do_expiration_with_current_time (BusExpireList *list,
       
   125                                  long           tv_sec,
       
   126                                  long           tv_usec)
       
   127 {
       
   128   DBusList *link;
       
   129   int next_interval;
       
   130 
       
   131   next_interval = -1;
       
   132   
       
   133   link = _dbus_list_get_first_link (&list->items);
       
   134   while (link != NULL)
       
   135     {
       
   136       DBusList *next = _dbus_list_get_next_link (&list->items, link);
       
   137       double elapsed;
       
   138       BusExpireItem *item;
       
   139 
       
   140       item = link->data;
       
   141 
       
   142       elapsed = ELAPSED_MILLISECONDS_SINCE (item->added_tv_sec,
       
   143                                             item->added_tv_usec,
       
   144                                             tv_sec, tv_usec);
       
   145 
       
   146       if (elapsed >= (double) list->expire_after)
       
   147         {
       
   148           _dbus_verbose ("Expiring an item %p\n", item);
       
   149 
       
   150           /* If the expire function fails, we just end up expiring
       
   151            * this item next time we walk through the list. This would
       
   152            * be an indeterminate time normally, so we set up the
       
   153            * next_interval to be "shortly" (just enough to avoid
       
   154            * a busy loop)
       
   155            */
       
   156           if (!(* list->expire_func) (list, link, list->data))
       
   157             {
       
   158               next_interval = _dbus_get_oom_wait ();
       
   159               break;
       
   160             }
       
   161         }
       
   162       else
       
   163         {
       
   164           /* We can end the loop, since the connections are in oldest-first order */
       
   165           next_interval = ((double)list->expire_after) - elapsed;
       
   166           _dbus_verbose ("Item %p expires in %d milliseconds\n",
       
   167                          item, next_interval);
       
   168 
       
   169           break;
       
   170         }
       
   171 
       
   172       link = next;
       
   173     }
       
   174 
       
   175   return next_interval;
       
   176 }
       
   177 
       
   178 static void
       
   179 bus_expirelist_expire (BusExpireList *list)
       
   180 {
       
   181   int next_interval;
       
   182 
       
   183   next_interval = -1;
       
   184 
       
   185   if (list->items != NULL)
       
   186     {
       
   187       long tv_sec, tv_usec;
       
   188 
       
   189       _dbus_get_current_time (&tv_sec, &tv_usec);
       
   190 
       
   191       next_interval = do_expiration_with_current_time (list, tv_sec, tv_usec);
       
   192     }
       
   193 
       
   194   bus_expire_timeout_set_interval (list->timeout, next_interval);
       
   195 }
       
   196 
       
   197 static dbus_bool_t
       
   198 expire_timeout_handler (void *data)
       
   199 {
       
   200   BusExpireList *list = data;
       
   201 
       
   202   _dbus_verbose ("Running %s\n", _DBUS_FUNCTION_NAME);
       
   203 
       
   204   /* note that this may remove the timeout */
       
   205   bus_expirelist_expire (list);
       
   206 
       
   207   return TRUE;
       
   208 }
       
   209 
       
   210 #ifdef DBUS_BUILD_TESTS
       
   211 
       
   212 typedef struct
       
   213 {
       
   214   BusExpireItem item;
       
   215   int expire_count;
       
   216 } TestExpireItem;
       
   217 
       
   218 static dbus_bool_t
       
   219 test_expire_func (BusExpireList *list,
       
   220                   DBusList      *link,
       
   221                   void          *data)
       
   222 {
       
   223   TestExpireItem *t;
       
   224 
       
   225   t = (TestExpireItem*) link->data;
       
   226 
       
   227   t->expire_count += 1;
       
   228 
       
   229   return TRUE;
       
   230 }
       
   231 
       
   232 static void
       
   233 time_add_milliseconds (long *tv_sec,
       
   234                        long *tv_usec,
       
   235                        int   milliseconds)
       
   236 {
       
   237   *tv_sec = *tv_sec + milliseconds / 1000;
       
   238   *tv_usec = *tv_usec + milliseconds * 1000;
       
   239   if (*tv_usec >= 1000000)
       
   240     {
       
   241       *tv_usec -= 1000000;
       
   242       *tv_sec += 1;
       
   243     }
       
   244 }
       
   245 
       
   246 dbus_bool_t
       
   247 bus_expire_list_test (const DBusString *test_data_dir)
       
   248 {
       
   249   DBusLoop *loop;
       
   250   BusExpireList *list;
       
   251   long tv_sec, tv_usec;
       
   252   long tv_sec_not_expired, tv_usec_not_expired;
       
   253   long tv_sec_expired, tv_usec_expired;
       
   254   long tv_sec_past, tv_usec_past;
       
   255   TestExpireItem *item;
       
   256   int next_interval;
       
   257   dbus_bool_t result = FALSE;
       
   258 
       
   259 
       
   260   loop = _dbus_loop_new ();
       
   261   _dbus_assert (loop != NULL);
       
   262 
       
   263 #define EXPIRE_AFTER 100
       
   264   
       
   265   list = bus_expire_list_new (loop, EXPIRE_AFTER,
       
   266                               test_expire_func, NULL);
       
   267   _dbus_assert (list != NULL);
       
   268 
       
   269   _dbus_get_current_time (&tv_sec, &tv_usec);
       
   270 
       
   271   tv_sec_not_expired = tv_sec;
       
   272   tv_usec_not_expired = tv_usec;
       
   273   time_add_milliseconds (&tv_sec_not_expired,
       
   274                          &tv_usec_not_expired, EXPIRE_AFTER - 1);
       
   275 
       
   276   tv_sec_expired = tv_sec;
       
   277   tv_usec_expired = tv_usec;
       
   278   time_add_milliseconds (&tv_sec_expired,
       
   279                          &tv_usec_expired, EXPIRE_AFTER);
       
   280   
       
   281 
       
   282   tv_sec_past = tv_sec - 1;
       
   283   tv_usec_past = tv_usec;
       
   284 
       
   285   item = dbus_new0 (TestExpireItem, 1);
       
   286 
       
   287   if (item == NULL)
       
   288     goto oom;
       
   289 
       
   290   item->item.added_tv_sec = tv_sec;
       
   291   item->item.added_tv_usec = tv_usec;
       
   292   if (!_dbus_list_append (&list->items, item))
       
   293     _dbus_assert_not_reached ("out of memory");
       
   294 
       
   295   next_interval =
       
   296     do_expiration_with_current_time (list, tv_sec_not_expired,
       
   297                                      tv_usec_not_expired);
       
   298   _dbus_assert (item->expire_count == 0);
       
   299   _dbus_verbose ("next_interval = %d\n", next_interval);
       
   300   _dbus_assert (next_interval == 1);
       
   301   
       
   302   next_interval =
       
   303     do_expiration_with_current_time (list, tv_sec_expired,
       
   304                                      tv_usec_expired);
       
   305   _dbus_assert (item->expire_count == 1);
       
   306   _dbus_verbose ("next_interval = %d\n", next_interval);
       
   307   _dbus_assert (next_interval == -1);
       
   308 
       
   309   next_interval =
       
   310     do_expiration_with_current_time (list, tv_sec_past,
       
   311                                      tv_usec_past);
       
   312   _dbus_assert (item->expire_count == 1);
       
   313   _dbus_verbose ("next_interval = %d\n", next_interval);
       
   314   _dbus_assert (next_interval == 1000 + EXPIRE_AFTER);
       
   315 
       
   316   _dbus_list_clear (&list->items);
       
   317   dbus_free (item);
       
   318   
       
   319   bus_expire_list_free (list);
       
   320   _dbus_loop_unref (loop);
       
   321   
       
   322   result = TRUE;
       
   323 
       
   324  oom:
       
   325   return result;
       
   326 }
       
   327 
       
   328 #endif /* DBUS_BUILD_TESTS */