D-Bus 1.2.24
|
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 #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 */