diff options
author | Thomas Martitz <kugel@rockbox.org> | 2011-11-17 17:55:02 +0000 |
---|---|---|
committer | Thomas Martitz <kugel@rockbox.org> | 2011-11-17 17:55:02 +0000 |
commit | 2a8eacdbfc5d98b016c480ddaddff100301f721f (patch) | |
tree | 69c79430463bb43c0e3ce96fbef851de6c35b277 /firmware/buflib.c | |
parent | 91206954aa0818e0790857f25f46a53d8e737a20 (diff) | |
download | rockbox-2a8eacdbfc5d98b016c480ddaddff100301f721f.tar.gz rockbox-2a8eacdbfc5d98b016c480ddaddff100301f721f.zip |
Buflib: Make shrinking and buflib_available() smarter.
* shrinking now considers freespace just before the alloc-to-be-shrinked,
that means less (or sometimes none at all) is taken from the audio buffer.
* core_available() now searches for the best free space, instead of simply the end,
i.e. it will not return 0 if the audio buffer is allocated and there's free space
before it. It also runs a compaction to ensure maximum contiguous memory.
audio_buffer_available() is also enhanced. It now considers the 256K reserve buffer,
and returns free buflib space instead if the audio buffer is short.
This all fixes the root problem of FS#12344 (Sansa Clip+: PANIC occurred when
dircache is enabled), that alloced from the audio buffer, even if it was very
short and buflib had many more available as free space before it.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31006 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/buflib.c')
-rw-r--r-- | firmware/buflib.c | 68 |
1 files changed, 62 insertions, 6 deletions
diff --git a/firmware/buflib.c b/firmware/buflib.c index 4ffd6cfce3..43fc4bd3de 100644 --- a/firmware/buflib.c +++ b/firmware/buflib.c | |||
@@ -89,6 +89,7 @@ | |||
89 | #define BDEBUGF(...) do { } while(0) | 89 | #define BDEBUGF(...) do { } while(0) |
90 | #endif | 90 | #endif |
91 | 91 | ||
92 | #define IS_MOVABLE(a) (!a[2].ops || a[2].ops->move_callback) | ||
92 | static union buflib_data* find_first_free(struct buflib_context *ctx); | 93 | static union buflib_data* find_first_free(struct buflib_context *ctx); |
93 | static union buflib_data* find_block_before(struct buflib_context *ctx, | 94 | static union buflib_data* find_block_before(struct buflib_context *ctx, |
94 | union buflib_data* block, | 95 | union buflib_data* block, |
@@ -198,7 +199,7 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift) | |||
198 | char* new_start; | 199 | char* new_start; |
199 | union buflib_data *new_block, *tmp = block[1].handle; | 200 | union buflib_data *new_block, *tmp = block[1].handle; |
200 | struct buflib_callbacks *ops = block[2].ops; | 201 | struct buflib_callbacks *ops = block[2].ops; |
201 | if (ops && !ops->move_callback) | 202 | if (!IS_MOVABLE(block)) |
202 | return false; | 203 | return false; |
203 | 204 | ||
204 | int handle = ctx->handle_table - tmp; | 205 | int handle = ctx->handle_table - tmp; |
@@ -312,8 +313,10 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints) | |||
312 | result = buflib_compact(ctx); | 313 | result = buflib_compact(ctx); |
313 | if (!result) | 314 | if (!result) |
314 | { | 315 | { |
315 | union buflib_data* this; | 316 | union buflib_data *this, *before; |
316 | for(this = ctx->buf_start; this < ctx->alloc_end; this += abs(this->val)) | 317 | for(this = ctx->buf_start, before = this; |
318 | this < ctx->alloc_end; | ||
319 | before = this, this += abs(this->val)) | ||
317 | { | 320 | { |
318 | if (this->val > 0 && this[2].ops | 321 | if (this->val > 0 && this[2].ops |
319 | && this[2].ops->shrink_callback) | 322 | && this[2].ops->shrink_callback) |
@@ -322,6 +325,20 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints) | |||
322 | int handle = ctx->handle_table - this[1].handle; | 325 | int handle = ctx->handle_table - this[1].handle; |
323 | char* data = this[1].handle->alloc; | 326 | char* data = this[1].handle->alloc; |
324 | bool last = (this+this->val) == ctx->alloc_end; | 327 | bool last = (this+this->val) == ctx->alloc_end; |
328 | unsigned pos_hints = shrink_hints & BUFLIB_SHRINK_POS_MASK; | ||
329 | /* adjust what we ask for if there's free space in the front | ||
330 | * this isn't too unlikely assuming this block is | ||
331 | * shrinkable but not movable */ | ||
332 | if (pos_hints == BUFLIB_SHRINK_POS_FRONT | ||
333 | && before != this && before->val < 0) | ||
334 | { | ||
335 | size_t free_space = (-before->val) * sizeof(union buflib_data); | ||
336 | size_t wanted = shrink_hints & BUFLIB_SHRINK_SIZE_MASK; | ||
337 | if (wanted < free_space) /* no shrink needed? */ | ||
338 | continue; | ||
339 | wanted -= free_space; | ||
340 | shrink_hints = pos_hints | wanted; | ||
341 | } | ||
325 | ret = this[2].ops->shrink_callback(handle, shrink_hints, | 342 | ret = this[2].ops->shrink_callback(handle, shrink_hints, |
326 | data, (char*)(this+this->val)-data); | 343 | data, (char*)(this+this->val)-data); |
327 | result |= (ret == BUFLIB_CB_OK); | 344 | result |= (ret == BUFLIB_CB_OK); |
@@ -598,9 +615,8 @@ buflib_free(struct buflib_context *ctx, int handle_num) | |||
598 | return 0; /* unconditionally */ | 615 | return 0; /* unconditionally */ |
599 | } | 616 | } |
600 | 617 | ||
601 | /* Return the maximum allocatable memory in bytes */ | 618 | static size_t |
602 | size_t | 619 | free_space_at_end(struct buflib_context* ctx) |
603 | buflib_available(struct buflib_context* ctx) | ||
604 | { | 620 | { |
605 | /* subtract 5 elements for | 621 | /* subtract 5 elements for |
606 | * val, handle, name_len, ops and the handle table entry*/ | 622 | * val, handle, name_len, ops and the handle table entry*/ |
@@ -615,6 +631,46 @@ buflib_available(struct buflib_context* ctx) | |||
615 | return 0; | 631 | return 0; |
616 | } | 632 | } |
617 | 633 | ||
634 | /* Return the maximum allocatable memory in bytes */ | ||
635 | size_t | ||
636 | buflib_available(struct buflib_context* ctx) | ||
637 | { | ||
638 | union buflib_data *this; | ||
639 | size_t free_space = 0, max_free_space = 0; | ||
640 | |||
641 | /* make sure buffer is as contiguous as possible */ | ||
642 | if (!ctx->compact) | ||
643 | buflib_compact(ctx); | ||
644 | |||
645 | /* now look if there's free in holes */ | ||
646 | for(this = find_first_free(ctx); this < ctx->alloc_end; this += abs(this->val)) | ||
647 | { | ||
648 | if (this->val < 0) | ||
649 | { | ||
650 | free_space += -this->val; | ||
651 | continue; | ||
652 | } | ||
653 | /* an unmovable section resets the count as free space | ||
654 | * can't be contigous */ | ||
655 | if (!IS_MOVABLE(this)) | ||
656 | { | ||
657 | if (max_free_space < free_space) | ||
658 | max_free_space = free_space; | ||
659 | free_space = 0; | ||
660 | } | ||
661 | } | ||
662 | |||
663 | /* select the best */ | ||
664 | max_free_space = MAX(max_free_space, free_space); | ||
665 | max_free_space *= sizeof(union buflib_data); | ||
666 | max_free_space = MAX(max_free_space, free_space_at_end(ctx)); | ||
667 | |||
668 | if (max_free_space > 0) | ||
669 | return max_free_space; | ||
670 | else | ||
671 | return 0; | ||
672 | } | ||
673 | |||
618 | /* | 674 | /* |
619 | * Allocate all available (as returned by buflib_available()) memory and return | 675 | * Allocate all available (as returned by buflib_available()) memory and return |
620 | * a handle to it | 676 | * a handle to it |