(linenum→info "unix/slp.c:2238")

dbus/1.0.2/dbus/dbus-dataslot.c

    1: /* -*- mode: C; c-file-style: "gnu" -*- */
    2: /* dbus-dataslot.c  storing data on objects
    3:  *
    4:  * Copyright (C) 2003 Red Hat, Inc.
    5:  *
    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: #include "dbus-dataslot.h"
   24: #include "dbus-threads-internal.h"
   25: 
   26: /**
   27:  * @defgroup DBusDataSlot Data slots
   28:  * @ingroup  DBusInternals
   29:  * @brief Storing data by ID
   30:  *
   31:  * Types and functions related to storing data by an
   32:  * allocated ID. This is used for dbus_connection_set_data(),
   33:  * dbus_server_set_data(), etc. 
   34:  * @{
   35:  */
   36: 
   37: /**
   38:  * Initializes a data slot allocator object, used to assign
   39:  * integer IDs for data slots.
   40:  *
   41:  * @param allocator the allocator to initialize
   42:  */
   43: dbus_bool_t
   44: _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator)
   45: {
   46:   allocator->allocated_slots = NULL;
   47:   allocator->n_allocated_slots = 0;
   48:   allocator->n_used_slots = 0;
   49:   allocator->lock_loc = NULL;
   50:   
   51:   return TRUE;
   52: }
   53: 
   54: /**
   55:  * Allocates an integer ID to be used for storing data
   56:  * in a #DBusDataSlotList. If the value at *slot_id_p is
   57:  * not -1, this function just increments the refcount for
   58:  * the existing slot ID. If the value is -1, a new slot ID
   59:  * is allocated and stored at *slot_id_p.
   60:  * 
   61:  * @param allocator the allocator
   62:  * @param mutex_loc the location lock for this allocator
   63:  * @param slot_id_p address to fill with the slot ID
   64:  * @returns #TRUE on success
   65:  */
   66: dbus_bool_t
   67: _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator,
   68:                                  DBusMutex             **mutex_loc,
   69:                                  dbus_int32_t          *slot_id_p)
   70: {
   71:   dbus_int32_t slot;
   72: 
   73:   _dbus_mutex_lock (*mutex_loc);
   74: 
   75:   if (allocator->n_allocated_slots == 0)
   76:     {
   77:       _dbus_assert (allocator->lock_loc == NULL);
   78:       allocator->lock_loc = mutex_loc;
   79:     }
   80:   else if (allocator->lock_loc != mutex_loc)
   81:     {
   82:       _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");
   83:       _dbus_assert_not_reached ("exiting");
   84:     }
   85: 
   86:   if (*slot_id_p >= 0)
   87:     {
   88:       slot = *slot_id_p;
   89:       
   90:       _dbus_assert (slot < allocator->n_allocated_slots);
   91:       _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
   92:       
   93:       allocator->allocated_slots[slot].refcount += 1;
   94: 
   95:       goto out;
   96:     }
   97: 
   98:   _dbus_assert (*slot_id_p < 0);
   99:   
  100:   if (allocator->n_used_slots < allocator->n_allocated_slots)
  101:     {
  102:       slot = 0;
  103:       while (slot < allocator->n_allocated_slots)
  104:         {
  105:           if (allocator->allocated_slots[slot].slot_id < 0)
  106:             {
  107:               allocator->allocated_slots[slot].slot_id = slot;
  108:               allocator->allocated_slots[slot].refcount = 1;
  109:               allocator->n_used_slots += 1;
  110:               break;
  111:             }
  112:           ++slot;
  113:         }
  114: 
  115:       _dbus_assert (slot < allocator->n_allocated_slots);
  116:     }
  117:   else
  118:     {
  119:       DBusAllocatedSlot *tmp;
  120:       
  121:       slot = -1;
  122:       tmp = dbus_realloc (allocator->allocated_slots,
  123:                           sizeof (DBusAllocatedSlot) * (allocator->n_allocated_slots + 1));
  124:       if (tmp == NULL)
  125:         goto out;
  126: 
  127:       allocator->allocated_slots = tmp;
  128:       slot = allocator->n_allocated_slots;
  129:       allocator->n_allocated_slots += 1;
  130:       allocator->n_used_slots += 1;
  131:       allocator->allocated_slots[slot].slot_id = slot;
  132:       allocator->allocated_slots[slot].refcount = 1;
  133:     }
  134: 
  135:   _dbus_assert (slot >= 0);
  136:   _dbus_assert (slot < allocator->n_allocated_slots);
  137:   _dbus_assert (*slot_id_p < 0);
  138:   _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
  139:   _dbus_assert (allocator->allocated_slots[slot].refcount == 1);
  140:   
  141:   *slot_id_p = slot;
  142:   
  143:   _dbus_verbose ("Allocated slot %d on allocator %p total %d slots allocated %d used\n",
  144:                  slot, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
  145:   
  146:  out:
  147:   _dbus_mutex_unlock (*(allocator->lock_loc));
  148:   return slot >= 0;
  149: }
  150: 
  151: /**
  152:  * Deallocates an ID previously allocated with
  153:  * _dbus_data_slot_allocator_alloc().  Existing data stored on
  154:  * existing #DBusDataSlotList objects with this ID will be freed when the
  155:  * data list is finalized, but may not be retrieved (and may only be
  156:  * replaced if someone else reallocates the slot).
  157:  * The slot value is reset to -1 if this is the last unref.
  158:  *
  159:  * @param allocator the allocator
  160:  * @param slot_id_p address where we store the slot
  161:  */
  162: void
  163: _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
  164:                                 dbus_int32_t          *slot_id_p)
  165: {
  166:   _dbus_mutex_lock (*(allocator->lock_loc));
  167:   
  168:   _dbus_assert (*slot_id_p < allocator->n_allocated_slots);
  169:   _dbus_assert (allocator->allocated_slots[*slot_id_p].slot_id == *slot_id_p);
  170:   _dbus_assert (allocator->allocated_slots[*slot_id_p].refcount > 0);
  171: 
  172:   allocator->allocated_slots[*slot_id_p].refcount -= 1;
  173: 
  174:   if (allocator->allocated_slots[*slot_id_p].refcount > 0)
  175:     {
  176:       _dbus_mutex_unlock (*(allocator->lock_loc));
  177:       return;
  178:     }
  179: 
  180:   /* refcount is 0, free the slot */
  181:   _dbus_verbose ("Freeing slot %d on allocator %p total %d allocated %d used\n",
  182:                  *slot_id_p, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
  183:   
  184:   allocator->allocated_slots[*slot_id_p].slot_id = -1;
  185:   *slot_id_p = -1;
  186:   
  187:   allocator->n_used_slots -= 1;
  188:   
  189:   if (allocator->n_used_slots == 0)
  190:     {
  191:       DBusMutex **mutex_loc = allocator->lock_loc;
  192:       
  193:       dbus_free (allocator->allocated_slots);
  194:       allocator->allocated_slots = NULL;
  195:       allocator->n_allocated_slots = 0;
  196:       allocator->lock_loc = NULL;
  197: 
  198:       _dbus_mutex_unlock (*mutex_loc);
  199:     }
  200:   else
  201:     {
  202:       _dbus_mutex_unlock (*(allocator->lock_loc));
  203:     }
  204: }
  205: 
  206: /**
  207:  * Initializes a slot list.
  208:  * @param list the list to initialize.
  209:  */
  210: void
  211: _dbus_data_slot_list_init (DBusDataSlotList *list)
  212: {
  213:   list->slots = NULL;
  214:   list->n_slots = 0;
  215: }
  216: 
  217: /**
  218:  * Stores a pointer in the data slot list, along with an optional
  219:  * function to be used for freeing the data when the data is set
  220:  * again, or when the slot list is finalized. The slot number must
  221:  * have been allocated with _dbus_data_slot_allocator_alloc() for the
  222:  * same allocator passed in here. The same allocator has to be used
  223:  * with the slot list every time.
  224:  *
  225:  * @param allocator the allocator to use
  226:  * @param list the data slot list
  227:  * @param slot the slot number
  228:  * @param data the data to store
  229:  * @param free_data_func finalizer function for the data
  230:  * @param old_free_func free function for any previously-existing data
  231:  * @param old_data previously-existing data, should be freed with old_free_func
  232:  * @returns #TRUE if there was enough memory to store the data
  233:  */
  234: dbus_bool_t
  235: _dbus_data_slot_list_set  (DBusDataSlotAllocator *allocator,
  236:                            DBusDataSlotList      *list,
  237:                            int                    slot,
  238:                            void                  *data,
  239:                            DBusFreeFunction       free_data_func,
  240:                            DBusFreeFunction      *old_free_func,
  241:                            void                 **old_data)
  242: {
  243: #ifndef DBUS_DISABLE_ASSERT
  244:   /* We need to take the allocator lock here, because the allocator could
  245:    * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
  246:    * are disabled, since then the asserts are empty.
  247:    */
  248:   _dbus_mutex_lock (*(allocator->lock_loc));
  249:   _dbus_assert (slot < allocator->n_allocated_slots);
  250:   _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
  251:   _dbus_mutex_unlock (*(allocator->lock_loc));
  252: #endif
  253:   
  254:   if (slot >= list->n_slots)
  255:     {
  256:       DBusDataSlot *tmp;
  257:       int i;
  258:       
  259:       tmp = dbus_realloc (list->slots,
  260:                           sizeof (DBusDataSlot) * (slot + 1));
  261:       if (tmp == NULL)
  262:         return FALSE;
  263:       
  264:       list->slots = tmp;
  265:       i = list->n_slots;
  266:       list->n_slots = slot + 1;
  267:       while (i < list->n_slots)
  268:         {
  269:           list->slots[i].data = NULL;
  270:           list->slots[i].free_data_func = NULL;
  271:           ++i;
  272:         }
  273:     }
  274: 
  275:   _dbus_assert (slot < list->n_slots);
  276: 
  277:   *old_data = list->slots[slot].data;
  278:   *old_free_func = list->slots[slot].free_data_func;
  279: 
  280:   list->slots[slot].data = data;
  281:   list->slots[slot].free_data_func = free_data_func;
  282: 
  283:   return TRUE;
  284: }
  285: 
  286: /**
  287:  * Retrieves data previously set with _dbus_data_slot_list_set_data().
  288:  * The slot must still be allocated (must not have been freed).
  289:  *
  290:  * @param allocator the allocator slot was allocated from
  291:  * @param list the data slot list
  292:  * @param slot the slot to get data from
  293:  * @returns the data, or #NULL if not found
  294:  */
  295: void*
  296: _dbus_data_slot_list_get  (DBusDataSlotAllocator *allocator,
  297:                            DBusDataSlotList      *list,
  298:                            int                    slot)
  299: {
  300: #ifndef DBUS_DISABLE_ASSERT
  301:   /* We need to take the allocator lock here, because the allocator could
  302:    * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
  303:    * are disabled, since then the asserts are empty.
  304:    */
  305:   _dbus_mutex_lock (*(allocator->lock_loc));
  306:   _dbus_assert (slot >= 0);
  307:   _dbus_assert (slot < allocator->n_allocated_slots);
  308:   _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
  309:   _dbus_mutex_unlock (*(allocator->lock_loc));
  310: #endif
  311: 
  312:   if (slot >= list->n_slots)
  313:     return NULL;
  314:   else
  315:     return list->slots[slot].data;
  316: }
  317: 
  318: /**
  319:  * Frees all data slots contained in the list, calling
  320:  * application-provided free functions if they exist.
  321:  *
  322:  * @param list the list to clear
  323:  */
  324: void
  325: _dbus_data_slot_list_clear (DBusDataSlotList *list)
  326: {
  327:   int i;
  328: 
  329:   i = 0;
  330:   while (i < list->n_slots)
  331:     {
  332:       if (list->slots[i].free_data_func)
  333:         (* list->slots[i].free_data_func) (list->slots[i].data);
  334:       list->slots[i].data = NULL;
  335:       list->slots[i].free_data_func = NULL;
  336:       ++i;
  337:     }
  338: }
  339: 
  340: /**
  341:  * Frees the data slot list and all data slots contained
  342:  * in it, calling application-provided free functions
  343:  * if they exist.
  344:  *
  345:  * @param list the list to free
  346:  */
  347: void
  348: _dbus_data_slot_list_free (DBusDataSlotList *list)
  349: {
  350:   _dbus_data_slot_list_clear (list);
  351:   
  352:   dbus_free (list->slots);
  353:   list->slots = NULL;
  354:   list->n_slots = 0;
  355: }
  356: 
  357: /** @} */
  358: 
  359: #ifdef DBUS_BUILD_TESTS
  360: #include "dbus-test.h"
  361: #include <stdio.h>
  362: 
  363: static int free_counter;
  364: 
  365: static void
  366: test_free_slot_data_func (void *data)
  367: {
  368:   int i = _DBUS_POINTER_TO_INT (data);
  369: 
  370:   _dbus_assert (free_counter == i);
  371:   ++free_counter;
  372: }
  373: 
  374: /**
  375:  * Test function for data slots
  376:  */
  377: dbus_bool_t
  378: _dbus_data_slot_test (void)
  379: {
  380:   DBusDataSlotAllocator allocator;
  381:   DBusDataSlotList list;
  382:   int i;
  383:   DBusFreeFunction old_free_func;
  384:   void *old_data;
  385:   DBusMutex *mutex;
  386:   
  387:   if (!_dbus_data_slot_allocator_init (&allocator))
  388:     _dbus_assert_not_reached ("no memory for allocator");
  389: 
  390:   _dbus_data_slot_list_init (&list);
  391: 
  392:   _dbus_mutex_new_at_location (&mutex);
  393:   if (mutex == NULL)
  394:     _dbus_assert_not_reached ("failed to alloc mutex");
  395:   
  396: #define N_SLOTS 100
  397: 
  398:   i = 0;
  399:   while (i < N_SLOTS)
  400:     {
  401:       /* we don't really want apps to rely on this ordered
  402:        * allocation, but it simplifies things to rely on it
  403:        * here.
  404:        */
  405:       dbus_int32_t tmp = -1;
  406:       
  407:       _dbus_data_slot_allocator_alloc (&allocator, &mutex, &tmp);
  408: 
  409:       if (tmp != i)
  410:         _dbus_assert_not_reached ("did not allocate slots in numeric order\n");
  411: 
  412:       ++i;
  413:     }
  414: 
  415:   i = 0;
  416:   while (i < N_SLOTS)
  417:     {
  418:       if (!_dbus_data_slot_list_set (&allocator, &list,
  419:                                      i,
  420:                                      _DBUS_INT_TO_POINTER (i), 
  421:                                      test_free_slot_data_func,
  422:                                      &old_free_func, &old_data))
  423:         _dbus_assert_not_reached ("no memory to set data");
  424: 
  425:       _dbus_assert (old_free_func == NULL);
  426:       _dbus_assert (old_data == NULL);
  427: 
  428:       _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) ==
  429:                     _DBUS_INT_TO_POINTER (i));
  430:       
  431:       ++i;
  432:     }
  433: 
  434:   free_counter = 0;
  435:   i = 0;
  436:   while (i < N_SLOTS)
  437:     {
  438:       if (!_dbus_data_slot_list_set (&allocator, &list,
  439:                                      i,
  440:                                      _DBUS_INT_TO_POINTER (i), 
  441:                                      test_free_slot_data_func,
  442:                                      &old_free_func, &old_data))
  443:         _dbus_assert_not_reached ("no memory to set data");
  444: 
  445:       _dbus_assert (old_free_func == test_free_slot_data_func);
  446:       _dbus_assert (_DBUS_POINTER_TO_INT (old_data) == i);
  447: 
  448:       (* old_free_func) (old_data);
  449:       _dbus_assert (i == (free_counter - 1));
  450: 
  451:       _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) ==
  452:                     _DBUS_INT_TO_POINTER (i));
  453:       
  454:       ++i;
  455:     }
  456: 
  457:   free_counter = 0;
  458:   _dbus_data_slot_list_free (&list);
  459: 
  460:   _dbus_assert (N_SLOTS == free_counter);
  461: 
  462:   i = 0;
  463:   while (i < N_SLOTS)
  464:     {
  465:       dbus_int32_t tmp = i;
  466:       
  467:       _dbus_data_slot_allocator_free (&allocator, &tmp);
  468:       _dbus_assert (tmp == -1);
  469:       ++i;
  470:     }
  471: 
  472:   _dbus_mutex_free_at_location (&mutex);
  473:   
  474:   return TRUE;
  475: }
  476: 
  477: #endif /* DBUS_BUILD_TESTS */
Syntax (Markdown)