dbus-dataslot.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 #include "dbus-dataslot.h"
00024 #include "dbus-threads-internal.h"
00025 
00043 dbus_bool_t
00044 _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator)
00045 {
00046   allocator->allocated_slots = NULL;
00047   allocator->n_allocated_slots = 0;
00048   allocator->n_used_slots = 0;
00049   allocator->lock_loc = NULL;
00050   
00051   return TRUE;
00052 }
00053 
00066 dbus_bool_t
00067 _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator,
00068                                  DBusMutex             **mutex_loc,
00069                                  dbus_int32_t          *slot_id_p)
00070 {
00071   dbus_int32_t slot;
00072 
00073   _dbus_mutex_lock (*mutex_loc);
00074 
00075   if (allocator->n_allocated_slots == 0)
00076     {
00077       _dbus_assert (allocator->lock_loc == NULL);
00078       allocator->lock_loc = mutex_loc;
00079     }
00080   else if (allocator->lock_loc != mutex_loc)
00081     {
00082       _dbus_warn_check_failed ("D-Bus threads were initialized after first using the D-Bus library. If your application does not directly initialize threads or use D-Bus, keep in mind that some library or plugin may have used D-Bus or initialized threads behind your back. You can often fix this problem by calling dbus_init_threads() or dbus_g_threads_init() early in your main() method, before D-Bus is used.\n");
00083       _dbus_assert_not_reached ("exiting");
00084     }
00085 
00086   if (*slot_id_p >= 0)
00087     {
00088       slot = *slot_id_p;
00089       
00090       _dbus_assert (slot < allocator->n_allocated_slots);
00091       _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
00092       
00093       allocator->allocated_slots[slot].refcount += 1;
00094 
00095       goto out;
00096     }
00097 
00098   _dbus_assert (*slot_id_p < 0);
00099   
00100   if (allocator->n_used_slots < allocator->n_allocated_slots)
00101     {
00102       slot = 0;
00103       while (slot < allocator->n_allocated_slots)
00104         {
00105           if (allocator->allocated_slots[slot].slot_id < 0)
00106             {
00107               allocator->allocated_slots[slot].slot_id = slot;
00108               allocator->allocated_slots[slot].refcount = 1;
00109               allocator->n_used_slots += 1;
00110               break;
00111             }
00112           ++slot;
00113         }
00114 
00115       _dbus_assert (slot < allocator->n_allocated_slots);
00116     }
00117   else
00118     {
00119       DBusAllocatedSlot *tmp;
00120       
00121       slot = -1;
00122       tmp = dbus_realloc (allocator->allocated_slots,
00123                           sizeof (DBusAllocatedSlot) * (allocator->n_allocated_slots + 1));
00124       if (tmp == NULL)
00125         goto out;
00126 
00127       allocator->allocated_slots = tmp;
00128       slot = allocator->n_allocated_slots;
00129       allocator->n_allocated_slots += 1;
00130       allocator->n_used_slots += 1;
00131       allocator->allocated_slots[slot].slot_id = slot;
00132       allocator->allocated_slots[slot].refcount = 1;
00133     }
00134 
00135   _dbus_assert (slot >= 0);
00136   _dbus_assert (slot < allocator->n_allocated_slots);
00137   _dbus_assert (*slot_id_p < 0);
00138   _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
00139   _dbus_assert (allocator->allocated_slots[slot].refcount == 1);
00140   
00141   *slot_id_p = slot;
00142   
00143   _dbus_verbose ("Allocated slot %d on allocator %p total %d slots allocated %d used\n",
00144                  slot, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
00145   
00146  out:
00147   _dbus_mutex_unlock (*(allocator->lock_loc));
00148   return slot >= 0;
00149 }
00150 
00162 void
00163 _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
00164                                 dbus_int32_t          *slot_id_p)
00165 {
00166   _dbus_mutex_lock (*(allocator->lock_loc));
00167   
00168   _dbus_assert (*slot_id_p < allocator->n_allocated_slots);
00169   _dbus_assert (allocator->allocated_slots[*slot_id_p].slot_id == *slot_id_p);
00170   _dbus_assert (allocator->allocated_slots[*slot_id_p].refcount > 0);
00171 
00172   allocator->allocated_slots[*slot_id_p].refcount -= 1;
00173 
00174   if (allocator->allocated_slots[*slot_id_p].refcount > 0)
00175     {
00176       _dbus_mutex_unlock (*(allocator->lock_loc));
00177       return;
00178     }
00179 
00180   /* refcount is 0, free the slot */
00181   _dbus_verbose ("Freeing slot %d on allocator %p total %d allocated %d used\n",
00182                  *slot_id_p, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
00183   
00184   allocator->allocated_slots[*slot_id_p].slot_id = -1;
00185   *slot_id_p = -1;
00186   
00187   allocator->n_used_slots -= 1;
00188   
00189   if (allocator->n_used_slots == 0)
00190     {
00191       DBusMutex **mutex_loc = allocator->lock_loc;
00192       
00193       dbus_free (allocator->allocated_slots);
00194       allocator->allocated_slots = NULL;
00195       allocator->n_allocated_slots = 0;
00196       allocator->lock_loc = NULL;
00197 
00198       _dbus_mutex_unlock (*mutex_loc);
00199     }
00200   else
00201     {
00202       _dbus_mutex_unlock (*(allocator->lock_loc));
00203     }
00204 }
00205 
00210 void
00211 _dbus_data_slot_list_init (DBusDataSlotList *list)
00212 {
00213   list->slots = NULL;
00214   list->n_slots = 0;
00215 }
00216 
00234 dbus_bool_t
00235 _dbus_data_slot_list_set  (DBusDataSlotAllocator *allocator,
00236                            DBusDataSlotList      *list,
00237                            int                    slot,
00238                            void                  *data,
00239                            DBusFreeFunction       free_data_func,
00240                            DBusFreeFunction      *old_free_func,
00241                            void                 **old_data)
00242 {
00243 #ifndef DBUS_DISABLE_ASSERT
00244   /* We need to take the allocator lock here, because the allocator could
00245    * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
00246    * are disabled, since then the asserts are empty.
00247    */
00248   _dbus_mutex_lock (*(allocator->lock_loc));
00249   _dbus_assert (slot < allocator->n_allocated_slots);
00250   _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
00251   _dbus_mutex_unlock (*(allocator->lock_loc));
00252 #endif
00253   
00254   if (slot >= list->n_slots)
00255     {
00256       DBusDataSlot *tmp;
00257       int i;
00258       
00259       tmp = dbus_realloc (list->slots,
00260                           sizeof (DBusDataSlot) * (slot + 1));
00261       if (tmp == NULL)
00262         return FALSE;
00263       
00264       list->slots = tmp;
00265       i = list->n_slots;
00266       list->n_slots = slot + 1;
00267       while (i < list->n_slots)
00268         {
00269           list->slots[i].data = NULL;
00270           list->slots[i].free_data_func = NULL;
00271           ++i;
00272         }
00273     }
00274 
00275   _dbus_assert (slot < list->n_slots);
00276 
00277   *old_data = list->slots[slot].data;
00278   *old_free_func = list->slots[slot].free_data_func;
00279 
00280   list->slots[slot].data = data;
00281   list->slots[slot].free_data_func = free_data_func;
00282 
00283   return TRUE;
00284 }
00285 
00295 void*
00296 _dbus_data_slot_list_get  (DBusDataSlotAllocator *allocator,
00297                            DBusDataSlotList      *list,
00298                            int                    slot)
00299 {
00300 #ifndef DBUS_DISABLE_ASSERT
00301   /* We need to take the allocator lock here, because the allocator could
00302    * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
00303    * are disabled, since then the asserts are empty.
00304    */
00305   _dbus_mutex_lock (*(allocator->lock_loc));
00306   _dbus_assert (slot >= 0);
00307   _dbus_assert (slot < allocator->n_allocated_slots);
00308   _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
00309   _dbus_mutex_unlock (*(allocator->lock_loc));
00310 #endif
00311 
00312   if (slot >= list->n_slots)
00313     return NULL;
00314   else
00315     return list->slots[slot].data;
00316 }
00317 
00324 void
00325 _dbus_data_slot_list_clear (DBusDataSlotList *list)
00326 {
00327   int i;
00328 
00329   i = 0;
00330   while (i < list->n_slots)
00331     {
00332       if (list->slots[i].free_data_func)
00333         (* list->slots[i].free_data_func) (list->slots[i].data);
00334       list->slots[i].data = NULL;
00335       list->slots[i].free_data_func = NULL;
00336       ++i;
00337     }
00338 }
00339 
00347 void
00348 _dbus_data_slot_list_free (DBusDataSlotList *list)
00349 {
00350   _dbus_data_slot_list_clear (list);
00351   
00352   dbus_free (list->slots);
00353   list->slots = NULL;
00354   list->n_slots = 0;
00355 }
00356 
00359 #ifdef DBUS_BUILD_TESTS
00360 #include "dbus-test.h"
00361 #include <stdio.h>
00362 
00363 static int free_counter;
00364 
00365 static void
00366 test_free_slot_data_func (void *data)
00367 {
00368   int i = _DBUS_POINTER_TO_INT (data);
00369 
00370   _dbus_assert (free_counter == i);
00371   ++free_counter;
00372 }
00373 
00377 dbus_bool_t
00378 _dbus_data_slot_test (void)
00379 {
00380   DBusDataSlotAllocator allocator;
00381   DBusDataSlotList list;
00382   int i;
00383   DBusFreeFunction old_free_func;
00384   void *old_data;
00385   DBusMutex *mutex;
00386   
00387   if (!_dbus_data_slot_allocator_init (&allocator))
00388     _dbus_assert_not_reached ("no memory for allocator");
00389 
00390   _dbus_data_slot_list_init (&list);
00391 
00392   _dbus_mutex_new_at_location (&mutex);
00393   if (mutex == NULL)
00394     _dbus_assert_not_reached ("failed to alloc mutex");
00395   
00396 #define N_SLOTS 100
00397 
00398   i = 0;
00399   while (i < N_SLOTS)
00400     {
00401       /* we don't really want apps to rely on this ordered
00402        * allocation, but it simplifies things to rely on it
00403        * here.
00404        */
00405       dbus_int32_t tmp = -1;
00406       
00407       _dbus_data_slot_allocator_alloc (&allocator, &mutex, &tmp);
00408 
00409       if (tmp != i)
00410         _dbus_assert_not_reached ("did not allocate slots in numeric order\n");
00411 
00412       ++i;
00413     }
00414 
00415   i = 0;
00416   while (i < N_SLOTS)
00417     {
00418       if (!_dbus_data_slot_list_set (&allocator, &list,
00419                                      i,
00420                                      _DBUS_INT_TO_POINTER (i), 
00421                                      test_free_slot_data_func,
00422                                      &old_free_func, &old_data))
00423         _dbus_assert_not_reached ("no memory to set data");
00424 
00425       _dbus_assert (old_free_func == NULL);
00426       _dbus_assert (old_data == NULL);
00427 
00428       _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) ==
00429                     _DBUS_INT_TO_POINTER (i));
00430       
00431       ++i;
00432     }
00433 
00434   free_counter = 0;
00435   i = 0;
00436   while (i < N_SLOTS)
00437     {
00438       if (!_dbus_data_slot_list_set (&allocator, &list,
00439                                      i,
00440                                      _DBUS_INT_TO_POINTER (i), 
00441                                      test_free_slot_data_func,
00442                                      &old_free_func, &old_data))
00443         _dbus_assert_not_reached ("no memory to set data");
00444 
00445       _dbus_assert (old_free_func == test_free_slot_data_func);
00446       _dbus_assert (_DBUS_POINTER_TO_INT (old_data) == i);
00447 
00448       (* old_free_func) (old_data);
00449       _dbus_assert (i == (free_counter - 1));
00450 
00451       _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) ==
00452                     _DBUS_INT_TO_POINTER (i));
00453       
00454       ++i;
00455     }
00456 
00457   free_counter = 0;
00458   _dbus_data_slot_list_free (&list);
00459 
00460   _dbus_assert (N_SLOTS == free_counter);
00461 
00462   i = 0;
00463   while (i < N_SLOTS)
00464     {
00465       dbus_int32_t tmp = i;
00466       
00467       _dbus_data_slot_allocator_free (&allocator, &tmp);
00468       _dbus_assert (tmp == -1);
00469       ++i;
00470     }
00471 
00472   _dbus_mutex_free_at_location (&mutex);
00473   
00474   return TRUE;
00475 }
00476 
00477 #endif /* DBUS_BUILD_TESTS */

Generated on Fri Sep 21 18:12:11 2007 for D-Bus by  doxygen 1.5.1