D-Bus  1.10.12
dbus-dataslot.c
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 /* dbus-dataslot.c  storing data on objects
00003  *
00004  * Copyright (C) 2003 Red Hat, Inc.
00005  *
00006  * Licensed under the Academic Free License version 2.1
00007  * 
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  * 
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00021  *
00022  */
00023 
00024 #include <config.h>
00025 #include "dbus-dataslot.h"
00026 #include "dbus-threads-internal.h"
00027 
00045 dbus_bool_t
00046 _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator,
00047                                 DBusGlobalLock         lock)
00048 {
00049   allocator->allocated_slots = NULL;
00050   allocator->n_allocated_slots = 0;
00051   allocator->n_used_slots = 0;
00052   allocator->lock = lock;
00053 
00054   return TRUE;
00055 }
00056 
00068 dbus_bool_t
00069 _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator,
00070                                  dbus_int32_t          *slot_id_p)
00071 {
00072   dbus_int32_t slot;
00073 
00074   if (!_dbus_lock (allocator->lock))
00075     return FALSE;
00076 
00077   if (*slot_id_p >= 0)
00078     {
00079       slot = *slot_id_p;
00080       
00081       _dbus_assert (slot < allocator->n_allocated_slots);
00082       _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
00083       
00084       allocator->allocated_slots[slot].refcount += 1;
00085 
00086       goto out;
00087     }
00088 
00089   _dbus_assert (*slot_id_p < 0);
00090   
00091   if (allocator->n_used_slots < allocator->n_allocated_slots)
00092     {
00093       slot = 0;
00094       while (slot < allocator->n_allocated_slots)
00095         {
00096           if (allocator->allocated_slots[slot].slot_id < 0)
00097             {
00098               allocator->allocated_slots[slot].slot_id = slot;
00099               allocator->allocated_slots[slot].refcount = 1;
00100               allocator->n_used_slots += 1;
00101               break;
00102             }
00103           ++slot;
00104         }
00105 
00106       _dbus_assert (slot < allocator->n_allocated_slots);
00107     }
00108   else
00109     {
00110       DBusAllocatedSlot *tmp;
00111       
00112       slot = -1;
00113       tmp = dbus_realloc (allocator->allocated_slots,
00114                           sizeof (DBusAllocatedSlot) * (allocator->n_allocated_slots + 1));
00115       if (tmp == NULL)
00116         goto out;
00117 
00118       allocator->allocated_slots = tmp;
00119       slot = allocator->n_allocated_slots;
00120       allocator->n_allocated_slots += 1;
00121       allocator->n_used_slots += 1;
00122       allocator->allocated_slots[slot].slot_id = slot;
00123       allocator->allocated_slots[slot].refcount = 1;
00124     }
00125 
00126   _dbus_assert (slot >= 0);
00127   _dbus_assert (slot < allocator->n_allocated_slots);
00128   _dbus_assert (*slot_id_p < 0);
00129   _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
00130   _dbus_assert (allocator->allocated_slots[slot].refcount == 1);
00131   
00132   *slot_id_p = slot;
00133   
00134   _dbus_verbose ("Allocated slot %d on allocator %p total %d slots allocated %d used\n",
00135                  slot, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
00136   
00137  out:
00138   _dbus_unlock (allocator->lock);
00139   return slot >= 0;
00140 }
00141 
00153 void
00154 _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
00155                                 dbus_int32_t          *slot_id_p)
00156 {
00157   if (!_dbus_lock (allocator->lock))
00158     _dbus_assert_not_reached ("we should have initialized global locks "
00159         "before we allocated this slot");
00160 
00161   _dbus_assert (*slot_id_p < allocator->n_allocated_slots);
00162   _dbus_assert (allocator->allocated_slots[*slot_id_p].slot_id == *slot_id_p);
00163   _dbus_assert (allocator->allocated_slots[*slot_id_p].refcount > 0);
00164 
00165   allocator->allocated_slots[*slot_id_p].refcount -= 1;
00166 
00167   if (allocator->allocated_slots[*slot_id_p].refcount > 0)
00168     {
00169       _dbus_unlock (allocator->lock);
00170       return;
00171     }
00172 
00173   /* refcount is 0, free the slot */
00174   _dbus_verbose ("Freeing slot %d on allocator %p total %d allocated %d used\n",
00175                  *slot_id_p, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
00176   
00177   allocator->allocated_slots[*slot_id_p].slot_id = -1;
00178   *slot_id_p = -1;
00179   
00180   allocator->n_used_slots -= 1;
00181   
00182   if (allocator->n_used_slots == 0)
00183     {
00184       dbus_free (allocator->allocated_slots);
00185       allocator->allocated_slots = NULL;
00186       allocator->n_allocated_slots = 0;
00187     }
00188 
00189   _dbus_unlock (allocator->lock);
00190 }
00191 
00196 void
00197 _dbus_data_slot_list_init (DBusDataSlotList *list)
00198 {
00199   list->slots = NULL;
00200   list->n_slots = 0;
00201 }
00202 
00220 dbus_bool_t
00221 _dbus_data_slot_list_set  (DBusDataSlotAllocator *allocator,
00222                            DBusDataSlotList      *list,
00223                            int                    slot,
00224                            void                  *data,
00225                            DBusFreeFunction       free_data_func,
00226                            DBusFreeFunction      *old_free_func,
00227                            void                 **old_data)
00228 {
00229 #ifndef DBUS_DISABLE_ASSERT
00230   /* We need to take the allocator lock here, because the allocator could
00231    * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
00232    * are disabled, since then the asserts are empty.
00233    */
00234   if (!_dbus_lock (allocator->lock))
00235     _dbus_assert_not_reached ("we should have initialized global locks "
00236         "before we allocated this slot");
00237 
00238   _dbus_assert (slot < allocator->n_allocated_slots);
00239   _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
00240   _dbus_unlock (allocator->lock);
00241 #endif
00242   
00243   if (slot >= list->n_slots)
00244     {
00245       DBusDataSlot *tmp;
00246       int i;
00247       
00248       tmp = dbus_realloc (list->slots,
00249                           sizeof (DBusDataSlot) * (slot + 1));
00250       if (tmp == NULL)
00251         return FALSE;
00252       
00253       list->slots = tmp;
00254       i = list->n_slots;
00255       list->n_slots = slot + 1;
00256       while (i < list->n_slots)
00257         {
00258           list->slots[i].data = NULL;
00259           list->slots[i].free_data_func = NULL;
00260           ++i;
00261         }
00262     }
00263 
00264   _dbus_assert (slot < list->n_slots);
00265 
00266   *old_data = list->slots[slot].data;
00267   *old_free_func = list->slots[slot].free_data_func;
00268 
00269   list->slots[slot].data = data;
00270   list->slots[slot].free_data_func = free_data_func;
00271 
00272   return TRUE;
00273 }
00274 
00284 void*
00285 _dbus_data_slot_list_get  (DBusDataSlotAllocator *allocator,
00286                            DBusDataSlotList      *list,
00287                            int                    slot)
00288 {
00289 #ifndef DBUS_DISABLE_ASSERT
00290   /* We need to take the allocator lock here, because the allocator could
00291    * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
00292    * are disabled, since then the asserts are empty.
00293    */
00294   if (!_dbus_lock (allocator->lock))
00295     _dbus_assert_not_reached ("we should have initialized global locks "
00296         "before we allocated this slot");
00297 
00298   _dbus_assert (slot >= 0);
00299   _dbus_assert (slot < allocator->n_allocated_slots);
00300   _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
00301   _dbus_unlock (allocator->lock);
00302 #endif
00303 
00304   if (slot >= list->n_slots)
00305     return NULL;
00306   else
00307     return list->slots[slot].data;
00308 }
00309 
00316 void
00317 _dbus_data_slot_list_clear (DBusDataSlotList *list)
00318 {
00319   int i;
00320 
00321   i = 0;
00322   while (i < list->n_slots)
00323     {
00324       if (list->slots[i].free_data_func)
00325         (* list->slots[i].free_data_func) (list->slots[i].data);
00326       list->slots[i].data = NULL;
00327       list->slots[i].free_data_func = NULL;
00328       ++i;
00329     }
00330 }
00331 
00339 void
00340 _dbus_data_slot_list_free (DBusDataSlotList *list)
00341 {
00342   _dbus_data_slot_list_clear (list);
00343   
00344   dbus_free (list->slots);
00345   list->slots = NULL;
00346   list->n_slots = 0;
00347 }
00348 
00351 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00352 #include "dbus-test.h"
00353 #include <stdio.h>
00354 
00355 static int free_counter;
00356 
00357 static void
00358 test_free_slot_data_func (void *data)
00359 {
00360   int i = _DBUS_POINTER_TO_INT (data);
00361 
00362   _dbus_assert (free_counter == i);
00363   ++free_counter;
00364 }
00365 
00369 dbus_bool_t
00370 _dbus_data_slot_test (void)
00371 {
00372   DBusDataSlotAllocator allocator;
00373   DBusDataSlotList list;
00374   int i;
00375   DBusFreeFunction old_free_func;
00376   void *old_data;
00377 
00378   if (!_dbus_data_slot_allocator_init (&allocator, _DBUS_LOCK_server_slots))
00379     _dbus_assert_not_reached ("no memory for allocator");
00380 
00381   _dbus_data_slot_list_init (&list);
00382 
00383 #define N_SLOTS 100
00384 
00385   i = 0;
00386   while (i < N_SLOTS)
00387     {
00388       /* we don't really want apps to rely on this ordered
00389        * allocation, but it simplifies things to rely on it
00390        * here.
00391        */
00392       dbus_int32_t tmp = -1;
00393 
00394       _dbus_data_slot_allocator_alloc (&allocator, &tmp);
00395 
00396       if (tmp != i)
00397         _dbus_assert_not_reached ("did not allocate slots in numeric order\n");
00398 
00399       ++i;
00400     }
00401 
00402   i = 0;
00403   while (i < N_SLOTS)
00404     {
00405       if (!_dbus_data_slot_list_set (&allocator, &list,
00406                                      i,
00407                                      _DBUS_INT_TO_POINTER (i), 
00408                                      test_free_slot_data_func,
00409                                      &old_free_func, &old_data))
00410         _dbus_assert_not_reached ("no memory to set data");
00411 
00412       _dbus_assert (old_free_func == NULL);
00413       _dbus_assert (old_data == NULL);
00414 
00415       _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) ==
00416                     _DBUS_INT_TO_POINTER (i));
00417       
00418       ++i;
00419     }
00420 
00421   free_counter = 0;
00422   i = 0;
00423   while (i < N_SLOTS)
00424     {
00425       if (!_dbus_data_slot_list_set (&allocator, &list,
00426                                      i,
00427                                      _DBUS_INT_TO_POINTER (i), 
00428                                      test_free_slot_data_func,
00429                                      &old_free_func, &old_data))
00430         _dbus_assert_not_reached ("no memory to set data");
00431 
00432       _dbus_assert (old_free_func == test_free_slot_data_func);
00433       _dbus_assert (_DBUS_POINTER_TO_INT (old_data) == i);
00434 
00435       (* old_free_func) (old_data);
00436       _dbus_assert (i == (free_counter - 1));
00437 
00438       _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) ==
00439                     _DBUS_INT_TO_POINTER (i));
00440       
00441       ++i;
00442     }
00443 
00444   free_counter = 0;
00445   _dbus_data_slot_list_free (&list);
00446 
00447   _dbus_assert (N_SLOTS == free_counter);
00448 
00449   i = 0;
00450   while (i < N_SLOTS)
00451     {
00452       dbus_int32_t tmp = i;
00453       
00454       _dbus_data_slot_allocator_free (&allocator, &tmp);
00455       _dbus_assert (tmp == -1);
00456       ++i;
00457     }
00458 
00459   return TRUE;
00460 }
00461 
00462 #endif /* DBUS_ENABLE_EMBEDDED_TESTS */