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