summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2011-09-09 13:33:22 +0000
committerThomas Martitz <kugel@rockbox.org>2011-09-09 13:33:22 +0000
commit0dcbc6cd5d4d40b37f7dbc38a944bfe28901f6e0 (patch)
tree46be0072202c651d5b6ebfddd6a483d0697086b2
parent7e3a3f491d67cf2be3d89791c246826ae62d2a53 (diff)
downloadrockbox-0dcbc6cd5d4d40b37f7dbc38a944bfe28901f6e0.tar.gz
rockbox-0dcbc6cd5d4d40b37f7dbc38a944bfe28901f6e0.zip
Buflib: Handle not having enough space for new handles (fixes FS#12265).
Handles are allocated at the end, growing downwards. The tiny allocations from r30478 broke buflib, since it was assumed that the only shrinkable allocation (the audiobuffer) is the very last allocation. The tiny allocations however fit into the reserve buffer for new handles, breaking the above assumption, and they can't shrink to make room for handles. Now, move any allocations before audiobuf (or shrink audiobuf like before) to make room for handles. This also unifies some duplicated code. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30486 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/buflib.c67
1 files changed, 40 insertions, 27 deletions
diff --git a/firmware/buflib.c b/firmware/buflib.c
index d6cbf3049b..ac8cdc864c 100644
--- a/firmware/buflib.c
+++ b/firmware/buflib.c
@@ -89,6 +89,9 @@
89 #define BDEBUGF(...) do { } while(0) 89 #define BDEBUGF(...) do { } while(0)
90#endif 90#endif
91 91
92static union buflib_data*
93find_block_before(struct buflib_context *ctx, union buflib_data* block,
94 bool is_free);
92/* Initialize buffer manager */ 95/* Initialize buffer manager */
93void 96void
94buflib_init(struct buflib_context *ctx, void *buf, size_t size) 97buflib_init(struct buflib_context *ctx, void *buf, size_t size)
@@ -227,7 +230,7 @@ buflib_compact(struct buflib_context *ctx)
227 int shift = 0, len; 230 int shift = 0, len;
228 /* Store the results of attempting to shrink the handle table */ 231 /* Store the results of attempting to shrink the handle table */
229 bool ret = handle_table_shrink(ctx); 232 bool ret = handle_table_shrink(ctx);
230 for(block = ctx->first_free_block; block != ctx->alloc_end; block += len) 233 for(block = ctx->first_free_block; block < ctx->alloc_end; block += len)
231 { 234 {
232 len = block->val; 235 len = block->val;
233 /* This block is free, add its length to the shift value */ 236 /* This block is free, add its length to the shift value */
@@ -241,11 +244,14 @@ buflib_compact(struct buflib_context *ctx)
241 if (-ctx->first_free_block->val > block->val) 244 if (-ctx->first_free_block->val > block->val)
242 { 245 {
243 intptr_t size = ctx->first_free_block->val; 246 intptr_t size = ctx->first_free_block->val;
247 union buflib_data* next_block = block + block->val;
244 if (move_block(ctx, block, ctx->first_free_block - block)) 248 if (move_block(ctx, block, ctx->first_free_block - block))
245 { 249 {
246 /* moving was successful. Mark the next block as the new 250 /* moving was successful. Mark the next block as the new
247 * first_free_block and merge it with the free space 251 * first_free_block and merge it with the free space
248 * that the move created */ 252 * that the move created */
253 if (ctx->alloc_end == next_block)
254 ctx->alloc_end = block;
249 ctx->first_free_block += block->val; 255 ctx->first_free_block += block->val;
250 ctx->first_free_block->val = size + block->val; 256 ctx->first_free_block->val = size + block->val;
251 continue; 257 continue;
@@ -307,12 +313,16 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints)
307 int ret; 313 int ret;
308 int handle = ctx->handle_table - this[1].handle; 314 int handle = ctx->handle_table - this[1].handle;
309 char* data = this[1].handle->alloc; 315 char* data = this[1].handle->alloc;
316 bool last = (this+this->val) == ctx->alloc_end;
310 ret = this[2].ops->shrink_callback(handle, shrink_hints, 317 ret = this[2].ops->shrink_callback(handle, shrink_hints,
311 data, (char*)(this+this->val)-data); 318 data, (char*)(this+this->val)-data);
312 result |= (ret == BUFLIB_CB_OK); 319 result |= (ret == BUFLIB_CB_OK);
313 /* this might have changed in the callback (if 320 /* this might have changed in the callback (if
314 * it shrinked from the top), get it again */ 321 * it shrinked from the top), get it again */
315 this = handle_to_block(ctx, handle); 322 this = handle_to_block(ctx, handle);
323 /* could also change with shrinking from back */
324 if (last)
325 ctx->alloc_end = this + this->val;
316 } 326 }
317 } 327 }
318 /* shrinking was successful at least once, try compaction again */ 328 /* shrinking was successful at least once, try compaction again */
@@ -407,26 +417,26 @@ handle_alloc:
407 /* If allocation has failed, and compaction has succeded, it may be 417 /* If allocation has failed, and compaction has succeded, it may be
408 * possible to get a handle by trying again. 418 * possible to get a handle by trying again.
409 */ 419 */
410 if (!ctx->compact && buflib_compact(ctx)) 420 union buflib_data* last_block = find_block_before(ctx,
411 goto handle_alloc; 421 ctx->alloc_end, false);
412 else 422 struct buflib_callbacks* ops = last_block[2].ops;
413 { /* first try to shrink the alloc before the handle table 423 unsigned hints = 0;
414 * to make room for new handles */ 424 if (!ops || !ops->shrink_callback)
415 int handle = ctx->handle_table - ctx->last_handle; 425 { /* the last one isn't shrinkable
416 union buflib_data* last_block = handle_to_block(ctx, handle); 426 * make room in front of a shrinkable and move this alloc */
417 struct buflib_callbacks* ops = last_block[2].ops; 427 hints = BUFLIB_SHRINK_POS_FRONT;
418 if (ops && ops->shrink_callback) 428 hints |= last_block->val * sizeof(union buflib_data);
419 {
420 char *data = buflib_get_data(ctx, handle);
421 unsigned hint = BUFLIB_SHRINK_POS_BACK | 10*sizeof(union buflib_data);
422 if (ops->shrink_callback(handle, hint, data,
423 (char*)(last_block+last_block->val)-data) == BUFLIB_CB_OK)
424 { /* retry one more time */
425 goto handle_alloc;
426 }
427 }
428 return -1;
429 } 429 }
430 else if (ops && ops->shrink_callback)
431 { /* the last is shrinkable, make room for handles directly */
432 hints = BUFLIB_SHRINK_POS_BACK;
433 hints |= 16*sizeof(union buflib_data);
434 }
435 /* buflib_compact_and_shrink() will compact and move last_block()
436 * if possible */
437 if (buflib_compact_and_shrink(ctx, hints))
438 goto handle_alloc;
439 return -1;
430 } 440 }
431 441
432buffer_alloc: 442buffer_alloc:
@@ -504,9 +514,10 @@ buffer_alloc:
504 514
505/* Finds the free block before block, and returns NULL if it's not free */ 515/* Finds the free block before block, and returns NULL if it's not free */
506static union buflib_data* 516static union buflib_data*
507find_free_block_before(struct buflib_context *ctx, union buflib_data* block) 517find_block_before(struct buflib_context *ctx, union buflib_data* block,
518 bool is_free)
508{ 519{
509 union buflib_data *ret = ctx->first_free_block, 520 union buflib_data *ret = ctx->buf_start,
510 *next_block = ret; 521 *next_block = ret;
511 522
512 /* find the block that's before the current one */ 523 /* find the block that's before the current one */
@@ -519,10 +530,12 @@ find_free_block_before(struct buflib_context *ctx, union buflib_data* block)
519 /* If next_block == block, the above loop didn't go anywhere. If it did, 530 /* If next_block == block, the above loop didn't go anywhere. If it did,
520 * and the block before this one is empty, that is the wanted one 531 * and the block before this one is empty, that is the wanted one
521 */ 532 */
522 if (next_block == block && ret < block && ret->val < 0) 533 if (next_block == block && ret < block)
534 {
535 if (is_free && ret->val >= 0) /* NULL if found block isn't free */
536 return NULL;
523 return ret; 537 return ret;
524 /* otherwise, e.g. if ret > block, or if the buffer is compact, 538 }
525 * there's no free block before */
526 return NULL; 539 return NULL;
527} 540}
528 541
@@ -536,7 +549,7 @@ buflib_free(struct buflib_context *ctx, int handle_num)
536 /* We need to find the block before the current one, to see if it is free 549 /* We need to find the block before the current one, to see if it is free
537 * and can be merged with this one. 550 * and can be merged with this one.
538 */ 551 */
539 block = find_free_block_before(ctx, freed_block); 552 block = find_block_before(ctx, freed_block, true);
540 if (block) 553 if (block)
541 { 554 {
542 block->val -= freed_block->val; 555 block->val -= freed_block->val;
@@ -652,7 +665,7 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
652 /* mark the old block unallocated */ 665 /* mark the old block unallocated */
653 block->val = block - new_block; 666 block->val = block - new_block;
654 /* find the block before in order to merge with the new free space */ 667 /* find the block before in order to merge with the new free space */
655 union buflib_data *free_before = find_free_block_before(ctx, block); 668 union buflib_data *free_before = find_block_before(ctx, block, true);
656 if (free_before) 669 if (free_before)
657 free_before->val += block->val; 670 free_before->val += block->val;
658 else if (ctx->first_free_block > block) 671 else if (ctx->first_free_block > block)