From 2a8eacdbfc5d98b016c480ddaddff100301f721f Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Thu, 17 Nov 2011 17:55:02 +0000 Subject: [PATCH] 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 --- apps/menus/main_menu.c | 4 +-- apps/mpeg.c | 29 +++++++++++++----- apps/playback.c | 19 +++++++++--- firmware/buflib.c | 68 +++++++++++++++++++++++++++++++++++++---- firmware/export/audio.h | 4 +++ 5 files changed, 104 insertions(+), 20 deletions(-) diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c index 8053bf0cf8..bb8ea276fb 100644 --- a/apps/menus/main_menu.c +++ b/apps/menus/main_menu.c @@ -179,7 +179,7 @@ static const char* info_getname(int selected_item, void *data, case INFO_BUFFER: /* buffer */ { - long kib = audio_buffer_available() / 1024; /* to KiB */ + long kib = audio_buffer_size() / 1024; /* to KiB */ output_dyn_value(s1, sizeof(s1), kib, kbyte_units, true); snprintf(buffer, buffer_len, "%s %s", str(LANG_BUFFER_STAT), s1); } @@ -261,7 +261,7 @@ static int info_speak_item(int selected_item, void * data) case INFO_BUFFER: /* buffer */ { talk_id(LANG_BUFFER_STAT, false); - long kib = audio_buffer_available() / 1024; /* to KiB */ + long kib = audio_buffer_size() / 1024; /* to KiB */ output_dyn_value(NULL, 0, kib, kbyte_units, true); break; } diff --git a/apps/mpeg.c b/apps/mpeg.c index ae33ccc1bf..698695b72d 100644 --- a/apps/mpeg.c +++ b/apps/mpeg.c @@ -151,7 +151,7 @@ static bool paused; /* playback is paused */ static int audiobuf_handle; /* handle to the audio buffer */ static char* mpeg_audiobuf; /* poiunter to the audio buffer */ static long audiobuflen; /* length of the audio buffer */ - +#define AUDIO_BUFFER_RESERVE (256*1024) #ifdef SIMULATOR static char mpeg_stack[DEFAULT_STACK_SIZE]; static struct mp3entry taginfo; @@ -515,9 +515,16 @@ static void audio_reset_buffer_noalloc(void* buf, size_t bufsize); /* Buffer must not move. */ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size) { - long offset = audio_current_track()->offset; - bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY; + ssize_t extradata_size = old_size - audiobuflen; + /* check what buflib requests */ + size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); + ssize_t size = (ssize_t)old_size - wanted_size; + /* keep at least 256K for the buffering */ + if ((size - extradata_size) < AUDIO_BUFFER_RESERVE) + return BUFLIB_CB_CANNOT_SHRINK; /* TODO: Do it without stopping playback, if possible */ + bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY; + long offset = audio_current_track()->offset; /* don't call audio_hard_stop() as it frees this handle */ if (thread_self() == audio_thread_id) { /* inline case MPEG_STOP (audio_stop()) response @@ -528,9 +535,6 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s audio_stop(); talk_buffer_steal(); /* we obtain control over the buffer */ - /* we should be free to change the buffer now */ - size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); - ssize_t size = (ssize_t)old_size - wanted_size; switch (hints & BUFLIB_SHRINK_POS_MASK) { case BUFLIB_SHRINK_POS_BACK: @@ -2742,11 +2746,20 @@ void audio_set_recording_options(struct audio_recording_options *options) #endif /* SIMULATOR */ #endif /* CONFIG_CODEC == MAS3587F */ -size_t audio_buffer_available(void) +size_t audio_buffer_size(void) { if (audiobuf_handle > 0) return audiobuflen; - return core_available(); + return 0; +} + +size_t audio_buffer_available(void) +{ + size_t size = 0; + size_t core_size = core_available(); + if (audiobuf_handle > 0) + return audiobuflen - AUDIO_BUFFER_RESERVE - 128; + return MAX(core_size, size); } static void audio_reset_buffer_noalloc(void* buf, size_t bufsize) diff --git a/apps/playback.c b/apps/playback.c index a245091d91..d591998bec 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -733,13 +733,24 @@ static void scratch_mem_init(void *mem) } static int audiobuf_handle; +#define AUDIO_BUFFER_RESERVE (256*1024) static size_t filebuflen; + +size_t audio_buffer_size(void) +{ + if (audiobuf_handle > 0) + return filebuflen - AUDIO_BUFFER_RESERVE; + return 0; +} + size_t audio_buffer_available(void) { - if (audiobuf_handle > 0) /* if allocated return what we got */ - return filebuflen; - return core_available(); + size_t size = 0; + size_t core_size = core_available(); + if (audiobuf_handle > 0) /* if allocated return what we can give */ + size = filebuflen - AUDIO_BUFFER_RESERVE - 128; + return MAX(core_size, size); } /* Set up the audio buffer for playback @@ -840,7 +851,7 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); ssize_t size = (ssize_t)old_size - wanted_size; /* keep at least 256K for the buffering */ - if ((size - extradata_size) < 256*1024) + if ((size - extradata_size) < AUDIO_BUFFER_RESERVE) return BUFLIB_CB_CANNOT_SHRINK; 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 @@ #define BDEBUGF(...) do { } while(0) #endif +#define IS_MOVABLE(a) (!a[2].ops || a[2].ops->move_callback) static union buflib_data* find_first_free(struct buflib_context *ctx); static union buflib_data* find_block_before(struct buflib_context *ctx, union buflib_data* block, @@ -198,7 +199,7 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift) char* new_start; union buflib_data *new_block, *tmp = block[1].handle; struct buflib_callbacks *ops = block[2].ops; - if (ops && !ops->move_callback) + if (!IS_MOVABLE(block)) return false; int handle = ctx->handle_table - tmp; @@ -312,8 +313,10 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints) result = buflib_compact(ctx); if (!result) { - union buflib_data* this; - for(this = ctx->buf_start; this < ctx->alloc_end; this += abs(this->val)) + union buflib_data *this, *before; + for(this = ctx->buf_start, before = this; + this < ctx->alloc_end; + before = this, this += abs(this->val)) { if (this->val > 0 && this[2].ops && this[2].ops->shrink_callback) @@ -322,6 +325,20 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints) int handle = ctx->handle_table - this[1].handle; char* data = this[1].handle->alloc; bool last = (this+this->val) == ctx->alloc_end; + unsigned pos_hints = shrink_hints & BUFLIB_SHRINK_POS_MASK; + /* adjust what we ask for if there's free space in the front + * this isn't too unlikely assuming this block is + * shrinkable but not movable */ + if (pos_hints == BUFLIB_SHRINK_POS_FRONT + && before != this && before->val < 0) + { + size_t free_space = (-before->val) * sizeof(union buflib_data); + size_t wanted = shrink_hints & BUFLIB_SHRINK_SIZE_MASK; + if (wanted < free_space) /* no shrink needed? */ + continue; + wanted -= free_space; + shrink_hints = pos_hints | wanted; + } ret = this[2].ops->shrink_callback(handle, shrink_hints, data, (char*)(this+this->val)-data); result |= (ret == BUFLIB_CB_OK); @@ -598,9 +615,8 @@ buflib_free(struct buflib_context *ctx, int handle_num) return 0; /* unconditionally */ } -/* Return the maximum allocatable memory in bytes */ -size_t -buflib_available(struct buflib_context* ctx) +static size_t +free_space_at_end(struct buflib_context* ctx) { /* subtract 5 elements for * val, handle, name_len, ops and the handle table entry*/ @@ -615,6 +631,46 @@ buflib_available(struct buflib_context* ctx) return 0; } +/* Return the maximum allocatable memory in bytes */ +size_t +buflib_available(struct buflib_context* ctx) +{ + union buflib_data *this; + size_t free_space = 0, max_free_space = 0; + + /* make sure buffer is as contiguous as possible */ + if (!ctx->compact) + buflib_compact(ctx); + + /* now look if there's free in holes */ + for(this = find_first_free(ctx); this < ctx->alloc_end; this += abs(this->val)) + { + if (this->val < 0) + { + free_space += -this->val; + continue; + } + /* an unmovable section resets the count as free space + * can't be contigous */ + if (!IS_MOVABLE(this)) + { + if (max_free_space < free_space) + max_free_space = free_space; + free_space = 0; + } + } + + /* select the best */ + max_free_space = MAX(max_free_space, free_space); + max_free_space *= sizeof(union buflib_data); + max_free_space = MAX(max_free_space, free_space_at_end(ctx)); + + if (max_free_space > 0) + return max_free_space; + else + return 0; +} + /* * Allocate all available (as returned by buflib_available()) memory and return * a handle to it diff --git a/firmware/export/audio.h b/firmware/export/audio.h index 57f3c24aae..5309ddd1d1 100644 --- a/firmware/export/audio.h +++ b/firmware/export/audio.h @@ -58,6 +58,10 @@ void audio_resume(void); void audio_next(void); void audio_prev(void); int audio_status(void); +/* size of the audio buffer */ +size_t audio_buffer_size(void); +/* size of the buffer available for allocating memory from the audio buffer using core_*() + * returns core_available() if audio buffer is not allocated yet */ size_t audio_buffer_available(void); void audio_ff_rewind(long newpos); void audio_flush_and_reload_tracks(void);