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
This commit is contained in:
parent
91206954aa
commit
2a8eacdbfc
5 changed files with 104 additions and 20 deletions
|
@ -179,7 +179,7 @@ static const char* info_getname(int selected_item, void *data,
|
||||||
|
|
||||||
case INFO_BUFFER: /* buffer */
|
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);
|
output_dyn_value(s1, sizeof(s1), kib, kbyte_units, true);
|
||||||
snprintf(buffer, buffer_len, "%s %s", str(LANG_BUFFER_STAT), s1);
|
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 */
|
case INFO_BUFFER: /* buffer */
|
||||||
{
|
{
|
||||||
talk_id(LANG_BUFFER_STAT, false);
|
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);
|
output_dyn_value(NULL, 0, kib, kbyte_units, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
29
apps/mpeg.c
29
apps/mpeg.c
|
@ -151,7 +151,7 @@ static bool paused; /* playback is paused */
|
||||||
static int audiobuf_handle; /* handle to the audio buffer */
|
static int audiobuf_handle; /* handle to the audio buffer */
|
||||||
static char* mpeg_audiobuf; /* poiunter to the audio buffer */
|
static char* mpeg_audiobuf; /* poiunter to the audio buffer */
|
||||||
static long audiobuflen; /* length of the audio buffer */
|
static long audiobuflen; /* length of the audio buffer */
|
||||||
|
#define AUDIO_BUFFER_RESERVE (256*1024)
|
||||||
#ifdef SIMULATOR
|
#ifdef SIMULATOR
|
||||||
static char mpeg_stack[DEFAULT_STACK_SIZE];
|
static char mpeg_stack[DEFAULT_STACK_SIZE];
|
||||||
static struct mp3entry taginfo;
|
static struct mp3entry taginfo;
|
||||||
|
@ -515,9 +515,16 @@ static void audio_reset_buffer_noalloc(void* buf, size_t bufsize);
|
||||||
/* Buffer must not move. */
|
/* Buffer must not move. */
|
||||||
static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size)
|
static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size)
|
||||||
{
|
{
|
||||||
long offset = audio_current_track()->offset;
|
ssize_t extradata_size = old_size - audiobuflen;
|
||||||
bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY;
|
/* 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 */
|
/* 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 */
|
/* don't call audio_hard_stop() as it frees this handle */
|
||||||
if (thread_self() == audio_thread_id)
|
if (thread_self() == audio_thread_id)
|
||||||
{ /* inline case MPEG_STOP (audio_stop()) response
|
{ /* 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();
|
audio_stop();
|
||||||
talk_buffer_steal(); /* we obtain control over the buffer */
|
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)
|
switch (hints & BUFLIB_SHRINK_POS_MASK)
|
||||||
{
|
{
|
||||||
case BUFLIB_SHRINK_POS_BACK:
|
case BUFLIB_SHRINK_POS_BACK:
|
||||||
|
@ -2742,11 +2746,20 @@ void audio_set_recording_options(struct audio_recording_options *options)
|
||||||
#endif /* SIMULATOR */
|
#endif /* SIMULATOR */
|
||||||
#endif /* CONFIG_CODEC == MAS3587F */
|
#endif /* CONFIG_CODEC == MAS3587F */
|
||||||
|
|
||||||
size_t audio_buffer_available(void)
|
size_t audio_buffer_size(void)
|
||||||
{
|
{
|
||||||
if (audiobuf_handle > 0)
|
if (audiobuf_handle > 0)
|
||||||
return audiobuflen;
|
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)
|
static void audio_reset_buffer_noalloc(void* buf, size_t bufsize)
|
||||||
|
|
|
@ -733,13 +733,24 @@ static void scratch_mem_init(void *mem)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int audiobuf_handle;
|
static int audiobuf_handle;
|
||||||
|
#define AUDIO_BUFFER_RESERVE (256*1024)
|
||||||
static size_t filebuflen;
|
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)
|
size_t audio_buffer_available(void)
|
||||||
{
|
{
|
||||||
if (audiobuf_handle > 0) /* if allocated return what we got */
|
size_t size = 0;
|
||||||
return filebuflen;
|
size_t core_size = core_available();
|
||||||
return 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
|
/* 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);
|
size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
|
||||||
ssize_t size = (ssize_t)old_size - wanted_size;
|
ssize_t size = (ssize_t)old_size - wanted_size;
|
||||||
/* keep at least 256K for the buffering */
|
/* 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;
|
return BUFLIB_CB_CANNOT_SHRINK;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,7 @@
|
||||||
#define BDEBUGF(...) do { } while(0)
|
#define BDEBUGF(...) do { } while(0)
|
||||||
#endif
|
#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_first_free(struct buflib_context *ctx);
|
||||||
static union buflib_data* find_block_before(struct buflib_context *ctx,
|
static union buflib_data* find_block_before(struct buflib_context *ctx,
|
||||||
union buflib_data* block,
|
union buflib_data* block,
|
||||||
|
@ -198,7 +199,7 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
|
||||||
char* new_start;
|
char* new_start;
|
||||||
union buflib_data *new_block, *tmp = block[1].handle;
|
union buflib_data *new_block, *tmp = block[1].handle;
|
||||||
struct buflib_callbacks *ops = block[2].ops;
|
struct buflib_callbacks *ops = block[2].ops;
|
||||||
if (ops && !ops->move_callback)
|
if (!IS_MOVABLE(block))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int handle = ctx->handle_table - tmp;
|
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);
|
result = buflib_compact(ctx);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
union buflib_data* this;
|
union buflib_data *this, *before;
|
||||||
for(this = ctx->buf_start; this < ctx->alloc_end; this += abs(this->val))
|
for(this = ctx->buf_start, before = this;
|
||||||
|
this < ctx->alloc_end;
|
||||||
|
before = this, this += abs(this->val))
|
||||||
{
|
{
|
||||||
if (this->val > 0 && this[2].ops
|
if (this->val > 0 && this[2].ops
|
||||||
&& this[2].ops->shrink_callback)
|
&& 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;
|
int handle = ctx->handle_table - this[1].handle;
|
||||||
char* data = this[1].handle->alloc;
|
char* data = this[1].handle->alloc;
|
||||||
bool last = (this+this->val) == ctx->alloc_end;
|
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,
|
ret = this[2].ops->shrink_callback(handle, shrink_hints,
|
||||||
data, (char*)(this+this->val)-data);
|
data, (char*)(this+this->val)-data);
|
||||||
result |= (ret == BUFLIB_CB_OK);
|
result |= (ret == BUFLIB_CB_OK);
|
||||||
|
@ -598,9 +615,8 @@ buflib_free(struct buflib_context *ctx, int handle_num)
|
||||||
return 0; /* unconditionally */
|
return 0; /* unconditionally */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the maximum allocatable memory in bytes */
|
static size_t
|
||||||
size_t
|
free_space_at_end(struct buflib_context* ctx)
|
||||||
buflib_available(struct buflib_context* ctx)
|
|
||||||
{
|
{
|
||||||
/* subtract 5 elements for
|
/* subtract 5 elements for
|
||||||
* val, handle, name_len, ops and the handle table entry*/
|
* val, handle, name_len, ops and the handle table entry*/
|
||||||
|
@ -615,6 +631,46 @@ buflib_available(struct buflib_context* ctx)
|
||||||
return 0;
|
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
|
* Allocate all available (as returned by buflib_available()) memory and return
|
||||||
* a handle to it
|
* a handle to it
|
||||||
|
|
|
@ -58,6 +58,10 @@ void audio_resume(void);
|
||||||
void audio_next(void);
|
void audio_next(void);
|
||||||
void audio_prev(void);
|
void audio_prev(void);
|
||||||
int audio_status(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);
|
size_t audio_buffer_available(void);
|
||||||
void audio_ff_rewind(long newpos);
|
void audio_ff_rewind(long newpos);
|
||||||
void audio_flush_and_reload_tracks(void);
|
void audio_flush_and_reload_tracks(void);
|
||||||
|
|
Loading…
Reference in a new issue