|
D-Bus
1.5.8
|
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 00002 /* dbus-mempool.h Memory pools 00003 * 00004 * Copyright (C) 2002, 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-mempool.h" 00026 #include "dbus-internals.h" 00027 00053 typedef struct DBusFreedElement DBusFreedElement; 00054 00060 struct DBusFreedElement 00061 { 00062 DBusFreedElement *next; 00063 }; 00064 00069 #define ELEMENT_PADDING 4 00070 00075 typedef struct DBusMemBlock DBusMemBlock; 00076 00081 struct DBusMemBlock 00082 { 00083 DBusMemBlock *next; 00088 /* this is a long so that "elements" is aligned */ 00089 long used_so_far; 00091 unsigned char elements[ELEMENT_PADDING]; 00092 }; 00093 00097 struct DBusMemPool 00098 { 00099 int element_size; 00100 int block_size; 00101 unsigned int zero_elements : 1; 00103 DBusFreedElement *free_elements; 00104 DBusMemBlock *blocks; 00105 int allocated_elements; 00106 }; 00107 00136 DBusMemPool* 00137 _dbus_mem_pool_new (int element_size, 00138 dbus_bool_t zero_elements) 00139 { 00140 DBusMemPool *pool; 00141 00142 pool = dbus_new0 (DBusMemPool, 1); 00143 if (pool == NULL) 00144 return NULL; 00145 00146 /* Make the element size at least 8 bytes. */ 00147 if (element_size < 8) 00148 element_size = 8; 00149 00150 /* these assertions are equivalent but the first is more clear 00151 * to programmers that see it fail. 00152 */ 00153 _dbus_assert (element_size >= (int) sizeof (void*)); 00154 _dbus_assert (element_size >= (int) sizeof (DBusFreedElement)); 00155 00156 /* align the element size to a pointer boundary so we won't get bus 00157 * errors under other architectures. 00158 */ 00159 pool->element_size = _DBUS_ALIGN_VALUE (element_size, sizeof (void *)); 00160 00161 pool->zero_elements = zero_elements != FALSE; 00162 00163 pool->allocated_elements = 0; 00164 00165 /* pick a size for the first block; it increases 00166 * for each block we need to allocate. This is 00167 * actually half the initial block size 00168 * since _dbus_mem_pool_alloc() unconditionally 00169 * doubles it prior to creating a new block. */ 00170 pool->block_size = pool->element_size * 8; 00171 00172 _dbus_assert ((pool->block_size % 00173 pool->element_size) == 0); 00174 00175 return pool; 00176 } 00177 00183 void 00184 _dbus_mem_pool_free (DBusMemPool *pool) 00185 { 00186 DBusMemBlock *block; 00187 00188 block = pool->blocks; 00189 while (block != NULL) 00190 { 00191 DBusMemBlock *next = block->next; 00192 00193 dbus_free (block); 00194 00195 block = next; 00196 } 00197 00198 dbus_free (pool); 00199 } 00200 00208 void* 00209 _dbus_mem_pool_alloc (DBusMemPool *pool) 00210 { 00211 #ifdef DBUS_BUILD_TESTS 00212 if (_dbus_disable_mem_pools ()) 00213 { 00214 DBusMemBlock *block; 00215 int alloc_size; 00216 00217 /* This is obviously really silly, but it's 00218 * debug-mode-only code that is compiled out 00219 * when tests are disabled (_dbus_disable_mem_pools() 00220 * is a constant expression FALSE so this block 00221 * should vanish) 00222 */ 00223 00224 alloc_size = sizeof (DBusMemBlock) - ELEMENT_PADDING + 00225 pool->element_size; 00226 00227 if (pool->zero_elements) 00228 block = dbus_malloc0 (alloc_size); 00229 else 00230 block = dbus_malloc (alloc_size); 00231 00232 if (block != NULL) 00233 { 00234 block->next = pool->blocks; 00235 pool->blocks = block; 00236 pool->allocated_elements += 1; 00237 00238 return (void*) &block->elements[0]; 00239 } 00240 else 00241 return NULL; 00242 } 00243 else 00244 #endif 00245 { 00246 if (_dbus_decrement_fail_alloc_counter ()) 00247 { 00248 _dbus_verbose (" FAILING mempool alloc\n"); 00249 return NULL; 00250 } 00251 else if (pool->free_elements) 00252 { 00253 DBusFreedElement *element = pool->free_elements; 00254 00255 pool->free_elements = pool->free_elements->next; 00256 00257 if (pool->zero_elements) 00258 memset (element, '\0', pool->element_size); 00259 00260 pool->allocated_elements += 1; 00261 00262 return element; 00263 } 00264 else 00265 { 00266 void *element; 00267 00268 if (pool->blocks == NULL || 00269 pool->blocks->used_so_far == pool->block_size) 00270 { 00271 /* Need a new block */ 00272 DBusMemBlock *block; 00273 int alloc_size; 00274 #ifdef DBUS_BUILD_TESTS 00275 int saved_counter; 00276 #endif 00277 00278 if (pool->block_size <= _DBUS_INT_MAX / 4) /* avoid overflow */ 00279 { 00280 /* use a larger block size for our next block */ 00281 pool->block_size *= 2; 00282 _dbus_assert ((pool->block_size % 00283 pool->element_size) == 0); 00284 } 00285 00286 alloc_size = sizeof (DBusMemBlock) - ELEMENT_PADDING + pool->block_size; 00287 00288 #ifdef DBUS_BUILD_TESTS 00289 /* We save/restore the counter, so that memory pools won't 00290 * cause a given function to have different number of 00291 * allocations on different invocations. i.e. when testing 00292 * we want consistent alloc patterns. So we skip our 00293 * malloc here for purposes of failed alloc simulation. 00294 */ 00295 saved_counter = _dbus_get_fail_alloc_counter (); 00296 _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); 00297 #endif 00298 00299 if (pool->zero_elements) 00300 block = dbus_malloc0 (alloc_size); 00301 else 00302 block = dbus_malloc (alloc_size); 00303 00304 #ifdef DBUS_BUILD_TESTS 00305 _dbus_set_fail_alloc_counter (saved_counter); 00306 _dbus_assert (saved_counter == _dbus_get_fail_alloc_counter ()); 00307 #endif 00308 00309 if (block == NULL) 00310 return NULL; 00311 00312 block->used_so_far = 0; 00313 block->next = pool->blocks; 00314 pool->blocks = block; 00315 } 00316 00317 element = &pool->blocks->elements[pool->blocks->used_so_far]; 00318 00319 pool->blocks->used_so_far += pool->element_size; 00320 00321 pool->allocated_elements += 1; 00322 00323 return element; 00324 } 00325 } 00326 } 00327 00336 dbus_bool_t 00337 _dbus_mem_pool_dealloc (DBusMemPool *pool, 00338 void *element) 00339 { 00340 #ifdef DBUS_BUILD_TESTS 00341 if (_dbus_disable_mem_pools ()) 00342 { 00343 DBusMemBlock *block; 00344 DBusMemBlock *prev; 00345 00346 /* mmm, fast. ;-) debug-only code, so doesn't matter. */ 00347 00348 prev = NULL; 00349 block = pool->blocks; 00350 00351 while (block != NULL) 00352 { 00353 if (block->elements == (unsigned char*) element) 00354 { 00355 if (prev) 00356 prev->next = block->next; 00357 else 00358 pool->blocks = block->next; 00359 00360 dbus_free (block); 00361 00362 _dbus_assert (pool->allocated_elements > 0); 00363 pool->allocated_elements -= 1; 00364 00365 if (pool->allocated_elements == 0) 00366 _dbus_assert (pool->blocks == NULL); 00367 00368 return pool->blocks == NULL; 00369 } 00370 prev = block; 00371 block = block->next; 00372 } 00373 00374 _dbus_assert_not_reached ("freed nonexistent block"); 00375 return FALSE; 00376 } 00377 else 00378 #endif 00379 { 00380 DBusFreedElement *freed; 00381 00382 freed = element; 00383 freed->next = pool->free_elements; 00384 pool->free_elements = freed; 00385 00386 _dbus_assert (pool->allocated_elements > 0); 00387 pool->allocated_elements -= 1; 00388 00389 return pool->allocated_elements == 0; 00390 } 00391 } 00392 00393 #ifdef DBUS_ENABLE_STATS 00394 void 00395 _dbus_mem_pool_get_stats (DBusMemPool *pool, 00396 dbus_uint32_t *in_use_p, 00397 dbus_uint32_t *in_free_list_p, 00398 dbus_uint32_t *allocated_p) 00399 { 00400 DBusMemBlock *block; 00401 DBusFreedElement *freed; 00402 dbus_uint32_t in_use = 0; 00403 dbus_uint32_t in_free_list = 0; 00404 dbus_uint32_t allocated = 0; 00405 00406 if (pool != NULL) 00407 { 00408 in_use = pool->element_size * pool->allocated_elements; 00409 00410 for (freed = pool->free_elements; freed != NULL; freed = freed->next) 00411 { 00412 in_free_list += pool->element_size; 00413 } 00414 00415 for (block = pool->blocks; block != NULL; block = block->next) 00416 { 00417 if (block == pool->blocks) 00418 allocated += pool->block_size; 00419 else 00420 allocated += block->used_so_far; 00421 } 00422 } 00423 00424 if (in_use_p != NULL) 00425 *in_use_p = in_use; 00426 00427 if (in_free_list_p != NULL) 00428 *in_free_list_p = in_free_list; 00429 00430 if (allocated_p != NULL) 00431 *allocated_p = allocated; 00432 } 00433 #endif /* DBUS_ENABLE_STATS */ 00434 00437 #ifdef DBUS_BUILD_TESTS 00438 #include "dbus-test.h" 00439 #include <stdio.h> 00440 #include <time.h> 00441 00442 static void 00443 time_for_size (int size) 00444 { 00445 int i; 00446 int j; 00447 clock_t start; 00448 clock_t end; 00449 #define FREE_ARRAY_SIZE 512 00450 #define N_ITERATIONS FREE_ARRAY_SIZE * 512 00451 void *to_free[FREE_ARRAY_SIZE]; 00452 DBusMemPool *pool; 00453 00454 _dbus_verbose ("Timings for size %d\n", size); 00455 00456 _dbus_verbose (" malloc\n"); 00457 00458 start = clock (); 00459 00460 i = 0; 00461 j = 0; 00462 while (i < N_ITERATIONS) 00463 { 00464 to_free[j] = dbus_malloc (size); 00465 _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */ 00466 00467 ++j; 00468 00469 if (j == FREE_ARRAY_SIZE) 00470 { 00471 j = 0; 00472 while (j < FREE_ARRAY_SIZE) 00473 { 00474 dbus_free (to_free[j]); 00475 ++j; 00476 } 00477 00478 j = 0; 00479 } 00480 00481 ++i; 00482 } 00483 00484 end = clock (); 00485 00486 _dbus_verbose (" created/destroyed %d elements in %g seconds\n", 00487 N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC); 00488 00489 00490 00491 _dbus_verbose (" mempools\n"); 00492 00493 start = clock (); 00494 00495 pool = _dbus_mem_pool_new (size, FALSE); 00496 00497 i = 0; 00498 j = 0; 00499 while (i < N_ITERATIONS) 00500 { 00501 to_free[j] = _dbus_mem_pool_alloc (pool); 00502 _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */ 00503 00504 ++j; 00505 00506 if (j == FREE_ARRAY_SIZE) 00507 { 00508 j = 0; 00509 while (j < FREE_ARRAY_SIZE) 00510 { 00511 _dbus_mem_pool_dealloc (pool, to_free[j]); 00512 ++j; 00513 } 00514 00515 j = 0; 00516 } 00517 00518 ++i; 00519 } 00520 00521 _dbus_mem_pool_free (pool); 00522 00523 end = clock (); 00524 00525 _dbus_verbose (" created/destroyed %d elements in %g seconds\n", 00526 N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC); 00527 00528 _dbus_verbose (" zeroed malloc\n"); 00529 00530 start = clock (); 00531 00532 i = 0; 00533 j = 0; 00534 while (i < N_ITERATIONS) 00535 { 00536 to_free[j] = dbus_malloc0 (size); 00537 _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */ 00538 00539 ++j; 00540 00541 if (j == FREE_ARRAY_SIZE) 00542 { 00543 j = 0; 00544 while (j < FREE_ARRAY_SIZE) 00545 { 00546 dbus_free (to_free[j]); 00547 ++j; 00548 } 00549 00550 j = 0; 00551 } 00552 00553 ++i; 00554 } 00555 00556 end = clock (); 00557 00558 _dbus_verbose (" created/destroyed %d elements in %g seconds\n", 00559 N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC); 00560 00561 _dbus_verbose (" zeroed mempools\n"); 00562 00563 start = clock (); 00564 00565 pool = _dbus_mem_pool_new (size, TRUE); 00566 00567 i = 0; 00568 j = 0; 00569 while (i < N_ITERATIONS) 00570 { 00571 to_free[j] = _dbus_mem_pool_alloc (pool); 00572 _dbus_assert (to_free[j] != NULL); /* in a real app of course this is wrong */ 00573 00574 ++j; 00575 00576 if (j == FREE_ARRAY_SIZE) 00577 { 00578 j = 0; 00579 while (j < FREE_ARRAY_SIZE) 00580 { 00581 _dbus_mem_pool_dealloc (pool, to_free[j]); 00582 ++j; 00583 } 00584 00585 j = 0; 00586 } 00587 00588 ++i; 00589 } 00590 00591 _dbus_mem_pool_free (pool); 00592 00593 end = clock (); 00594 00595 _dbus_verbose (" created/destroyed %d elements in %g seconds\n", 00596 N_ITERATIONS, (end - start) / (double) CLOCKS_PER_SEC); 00597 } 00598 00604 dbus_bool_t 00605 _dbus_mem_pool_test (void) 00606 { 00607 int i; 00608 int element_sizes[] = { 4, 8, 16, 50, 124 }; 00609 00610 i = 0; 00611 while (i < _DBUS_N_ELEMENTS (element_sizes)) 00612 { 00613 time_for_size (element_sizes[i]); 00614 ++i; 00615 } 00616 00617 return TRUE; 00618 } 00619 00620 #endif /* DBUS_BUILD_TESTS */
1.7.5.1