1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-dataslot.c storing data on objects
4 * Copyright (C) 2003 Red Hat, Inc.
6 * Licensed under the Academic Free License version 2.1
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "dbus-dataslot.h"
26 #include "dbus-threads-internal.h"
29 * @defgroup DBusDataSlot Data slots
30 * @ingroup DBusInternals
31 * @brief Storing data by ID
33 * Types and functions related to storing data by an
34 * allocated ID. This is used for dbus_connection_set_data(),
35 * dbus_server_set_data(), etc.
40 * Initializes a data slot allocator object, used to assign
41 * integer IDs for data slots.
43 * @param allocator the allocator to initialize
46 _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator,
49 allocator->allocated_slots = NULL;
50 allocator->n_allocated_slots = 0;
51 allocator->n_used_slots = 0;
52 allocator->lock = lock;
58 * Allocates an integer ID to be used for storing data
59 * in a #DBusDataSlotList. If the value at *slot_id_p is
60 * not -1, this function just increments the refcount for
61 * the existing slot ID. If the value is -1, a new slot ID
62 * is allocated and stored at *slot_id_p.
64 * @param allocator the allocator
65 * @param slot_id_p address to fill with the slot ID
66 * @returns #TRUE on success
69 _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator,
70 dbus_int32_t *slot_id_p)
74 _dbus_lock (allocator->lock);
80 _dbus_assert (slot < allocator->n_allocated_slots);
81 _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
83 allocator->allocated_slots[slot].refcount += 1;
88 _dbus_assert (*slot_id_p < 0);
90 if (allocator->n_used_slots < allocator->n_allocated_slots)
93 while (slot < allocator->n_allocated_slots)
95 if (allocator->allocated_slots[slot].slot_id < 0)
97 allocator->allocated_slots[slot].slot_id = slot;
98 allocator->allocated_slots[slot].refcount = 1;
99 allocator->n_used_slots += 1;
105 _dbus_assert (slot < allocator->n_allocated_slots);
109 DBusAllocatedSlot *tmp;
112 tmp = dbus_realloc (allocator->allocated_slots,
113 sizeof (DBusAllocatedSlot) * (allocator->n_allocated_slots + 1));
117 allocator->allocated_slots = tmp;
118 slot = allocator->n_allocated_slots;
119 allocator->n_allocated_slots += 1;
120 allocator->n_used_slots += 1;
121 allocator->allocated_slots[slot].slot_id = slot;
122 allocator->allocated_slots[slot].refcount = 1;
125 _dbus_assert (slot >= 0);
126 _dbus_assert (slot < allocator->n_allocated_slots);
127 _dbus_assert (*slot_id_p < 0);
128 _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
129 _dbus_assert (allocator->allocated_slots[slot].refcount == 1);
133 _dbus_verbose ("Allocated slot %d on allocator %p total %d slots allocated %d used\n",
134 slot, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
137 _dbus_unlock (allocator->lock);
142 * Deallocates an ID previously allocated with
143 * _dbus_data_slot_allocator_alloc(). Existing data stored on
144 * existing #DBusDataSlotList objects with this ID will be freed when the
145 * data list is finalized, but may not be retrieved (and may only be
146 * replaced if someone else reallocates the slot).
147 * The slot value is reset to -1 if this is the last unref.
149 * @param allocator the allocator
150 * @param slot_id_p address where we store the slot
153 _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
154 dbus_int32_t *slot_id_p)
156 _dbus_lock (allocator->lock);
158 _dbus_assert (*slot_id_p < allocator->n_allocated_slots);
159 _dbus_assert (allocator->allocated_slots[*slot_id_p].slot_id == *slot_id_p);
160 _dbus_assert (allocator->allocated_slots[*slot_id_p].refcount > 0);
162 allocator->allocated_slots[*slot_id_p].refcount -= 1;
164 if (allocator->allocated_slots[*slot_id_p].refcount > 0)
166 _dbus_unlock (allocator->lock);
170 /* refcount is 0, free the slot */
171 _dbus_verbose ("Freeing slot %d on allocator %p total %d allocated %d used\n",
172 *slot_id_p, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
174 allocator->allocated_slots[*slot_id_p].slot_id = -1;
177 allocator->n_used_slots -= 1;
179 if (allocator->n_used_slots == 0)
181 dbus_free (allocator->allocated_slots);
182 allocator->allocated_slots = NULL;
183 allocator->n_allocated_slots = 0;
186 _dbus_unlock (allocator->lock);
190 * Initializes a slot list.
191 * @param list the list to initialize.
194 _dbus_data_slot_list_init (DBusDataSlotList *list)
201 * Stores a pointer in the data slot list, along with an optional
202 * function to be used for freeing the data when the data is set
203 * again, or when the slot list is finalized. The slot number must
204 * have been allocated with _dbus_data_slot_allocator_alloc() for the
205 * same allocator passed in here. The same allocator has to be used
206 * with the slot list every time.
208 * @param allocator the allocator to use
209 * @param list the data slot list
210 * @param slot the slot number
211 * @param data the data to store
212 * @param free_data_func finalizer function for the data
213 * @param old_free_func free function for any previously-existing data
214 * @param old_data previously-existing data, should be freed with old_free_func
215 * @returns #TRUE if there was enough memory to store the data
218 _dbus_data_slot_list_set (DBusDataSlotAllocator *allocator,
219 DBusDataSlotList *list,
222 DBusFreeFunction free_data_func,
223 DBusFreeFunction *old_free_func,
226 #ifndef DBUS_DISABLE_ASSERT
227 /* We need to take the allocator lock here, because the allocator could
228 * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
229 * are disabled, since then the asserts are empty.
231 _dbus_lock (allocator->lock);
232 _dbus_assert (slot < allocator->n_allocated_slots);
233 _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
234 _dbus_unlock (allocator->lock);
237 if (slot >= list->n_slots)
242 tmp = dbus_realloc (list->slots,
243 sizeof (DBusDataSlot) * (slot + 1));
249 list->n_slots = slot + 1;
250 while (i < list->n_slots)
252 list->slots[i].data = NULL;
253 list->slots[i].free_data_func = NULL;
258 _dbus_assert (slot < list->n_slots);
260 *old_data = list->slots[slot].data;
261 *old_free_func = list->slots[slot].free_data_func;
263 list->slots[slot].data = data;
264 list->slots[slot].free_data_func = free_data_func;
270 * Retrieves data previously set with _dbus_data_slot_list_set_data().
271 * The slot must still be allocated (must not have been freed).
273 * @param allocator the allocator slot was allocated from
274 * @param list the data slot list
275 * @param slot the slot to get data from
276 * @returns the data, or #NULL if not found
279 _dbus_data_slot_list_get (DBusDataSlotAllocator *allocator,
280 DBusDataSlotList *list,
283 #ifndef DBUS_DISABLE_ASSERT
284 /* We need to take the allocator lock here, because the allocator could
285 * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
286 * are disabled, since then the asserts are empty.
288 _dbus_lock (allocator->lock);
289 _dbus_assert (slot >= 0);
290 _dbus_assert (slot < allocator->n_allocated_slots);
291 _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
292 _dbus_unlock (allocator->lock);
295 if (slot >= list->n_slots)
298 return list->slots[slot].data;
302 * Frees all data slots contained in the list, calling
303 * application-provided free functions if they exist.
305 * @param list the list to clear
308 _dbus_data_slot_list_clear (DBusDataSlotList *list)
313 while (i < list->n_slots)
315 if (list->slots[i].free_data_func)
316 (* list->slots[i].free_data_func) (list->slots[i].data);
317 list->slots[i].data = NULL;
318 list->slots[i].free_data_func = NULL;
324 * Frees the data slot list and all data slots contained
325 * in it, calling application-provided free functions
328 * @param list the list to free
331 _dbus_data_slot_list_free (DBusDataSlotList *list)
333 _dbus_data_slot_list_clear (list);
335 dbus_free (list->slots);
342 #ifdef DBUS_BUILD_TESTS
343 #include "dbus-test.h"
346 static int free_counter;
349 test_free_slot_data_func (void *data)
351 int i = _DBUS_POINTER_TO_INT (data);
353 _dbus_assert (free_counter == i);
358 * Test function for data slots
361 _dbus_data_slot_test (void)
363 DBusDataSlotAllocator allocator;
364 DBusDataSlotList list;
366 DBusFreeFunction old_free_func;
369 if (!_dbus_data_slot_allocator_init (&allocator, _DBUS_LOCK_server_slots))
370 _dbus_assert_not_reached ("no memory for allocator");
372 _dbus_data_slot_list_init (&list);
379 /* we don't really want apps to rely on this ordered
380 * allocation, but it simplifies things to rely on it
383 dbus_int32_t tmp = -1;
385 _dbus_data_slot_allocator_alloc (&allocator, &tmp);
388 _dbus_assert_not_reached ("did not allocate slots in numeric order\n");
396 if (!_dbus_data_slot_list_set (&allocator, &list,
398 _DBUS_INT_TO_POINTER (i),
399 test_free_slot_data_func,
400 &old_free_func, &old_data))
401 _dbus_assert_not_reached ("no memory to set data");
403 _dbus_assert (old_free_func == NULL);
404 _dbus_assert (old_data == NULL);
406 _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) ==
407 _DBUS_INT_TO_POINTER (i));
416 if (!_dbus_data_slot_list_set (&allocator, &list,
418 _DBUS_INT_TO_POINTER (i),
419 test_free_slot_data_func,
420 &old_free_func, &old_data))
421 _dbus_assert_not_reached ("no memory to set data");
423 _dbus_assert (old_free_func == test_free_slot_data_func);
424 _dbus_assert (_DBUS_POINTER_TO_INT (old_data) == i);
426 (* old_free_func) (old_data);
427 _dbus_assert (i == (free_counter - 1));
429 _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) ==
430 _DBUS_INT_TO_POINTER (i));
436 _dbus_data_slot_list_free (&list);
438 _dbus_assert (N_SLOTS == free_counter);
443 dbus_int32_t tmp = i;
445 _dbus_data_slot_allocator_free (&allocator, &tmp);
446 _dbus_assert (tmp == -1);
453 #endif /* DBUS_BUILD_TESTS */