summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2011-11-17 17:55:02 +0000
committerThomas Martitz <kugel@rockbox.org>2011-11-17 17:55:02 +0000
commit2a8eacdbfc5d98b016c480ddaddff100301f721f (patch)
tree69c79430463bb43c0e3ce96fbef851de6c35b277
parent91206954aa0818e0790857f25f46a53d8e737a20 (diff)
downloadrockbox-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
-rw-r--r--apps/menus/main_menu.c4
-rw-r--r--apps/mpeg.c29
-rw-r--r--apps/playback.c19
-rw-r--r--firmware/buflib.c68
-rw-r--r--firmware/export/audio.h4
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,
179 179
180 case INFO_BUFFER: /* buffer */ 180 case INFO_BUFFER: /* buffer */
181 { 181 {
182 long kib = audio_buffer_available() / 1024; /* to KiB */ 182 long kib = audio_buffer_size() / 1024; /* to KiB */
183 output_dyn_value(s1, sizeof(s1), kib, kbyte_units, true); 183 output_dyn_value(s1, sizeof(s1), kib, kbyte_units, true);
184 snprintf(buffer, buffer_len, "%s %s", str(LANG_BUFFER_STAT), s1); 184 snprintf(buffer, buffer_len, "%s %s", str(LANG_BUFFER_STAT), s1);
185 } 185 }
@@ -261,7 +261,7 @@ static int info_speak_item(int selected_item, void * data)
261 case INFO_BUFFER: /* buffer */ 261 case INFO_BUFFER: /* buffer */
262 { 262 {
263 talk_id(LANG_BUFFER_STAT, false); 263 talk_id(LANG_BUFFER_STAT, false);
264 long kib = audio_buffer_available() / 1024; /* to KiB */ 264 long kib = audio_buffer_size() / 1024; /* to KiB */
265 output_dyn_value(NULL, 0, kib, kbyte_units, true); 265 output_dyn_value(NULL, 0, kib, kbyte_units, true);
266 break; 266 break;
267 } 267 }
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 */
151static int audiobuf_handle; /* handle to the audio buffer */ 151static int audiobuf_handle; /* handle to the audio buffer */
152static char* mpeg_audiobuf; /* poiunter to the audio buffer */ 152static char* mpeg_audiobuf; /* poiunter to the audio buffer */
153static long audiobuflen; /* length of the audio buffer */ 153static long audiobuflen; /* length of the audio buffer */
154 154#define AUDIO_BUFFER_RESERVE (256*1024)
155#ifdef SIMULATOR 155#ifdef SIMULATOR
156static char mpeg_stack[DEFAULT_STACK_SIZE]; 156static char mpeg_stack[DEFAULT_STACK_SIZE];
157static struct mp3entry taginfo; 157static struct mp3entry taginfo;
@@ -515,9 +515,16 @@ static void audio_reset_buffer_noalloc(void* buf, size_t bufsize);
515/* Buffer must not move. */ 515/* Buffer must not move. */
516static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size) 516static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size)
517{ 517{
518 long offset = audio_current_track()->offset; 518 ssize_t extradata_size = old_size - audiobuflen;
519 bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY; 519 /* check what buflib requests */
520 size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
521 ssize_t size = (ssize_t)old_size - wanted_size;
522 /* keep at least 256K for the buffering */
523 if ((size - extradata_size) < AUDIO_BUFFER_RESERVE)
524 return BUFLIB_CB_CANNOT_SHRINK;
520 /* TODO: Do it without stopping playback, if possible */ 525 /* TODO: Do it without stopping playback, if possible */
526 bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY;
527 long offset = audio_current_track()->offset;
521 /* don't call audio_hard_stop() as it frees this handle */ 528 /* don't call audio_hard_stop() as it frees this handle */
522 if (thread_self() == audio_thread_id) 529 if (thread_self() == audio_thread_id)
523 { /* inline case MPEG_STOP (audio_stop()) response 530 { /* 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
528 audio_stop(); 535 audio_stop();
529 talk_buffer_steal(); /* we obtain control over the buffer */ 536 talk_buffer_steal(); /* we obtain control over the buffer */
530 537
531 /* we should be free to change the buffer now */
532 size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
533 ssize_t size = (ssize_t)old_size - wanted_size;
534 switch (hints & BUFLIB_SHRINK_POS_MASK) 538 switch (hints & BUFLIB_SHRINK_POS_MASK)
535 { 539 {
536 case BUFLIB_SHRINK_POS_BACK: 540 case BUFLIB_SHRINK_POS_BACK:
@@ -2742,11 +2746,20 @@ void audio_set_recording_options(struct audio_recording_options *options)
2742#endif /* SIMULATOR */ 2746#endif /* SIMULATOR */
2743#endif /* CONFIG_CODEC == MAS3587F */ 2747#endif /* CONFIG_CODEC == MAS3587F */
2744 2748
2745size_t audio_buffer_available(void) 2749size_t audio_buffer_size(void)
2746{ 2750{
2747 if (audiobuf_handle > 0) 2751 if (audiobuf_handle > 0)
2748 return audiobuflen; 2752 return audiobuflen;
2749 return core_available(); 2753 return 0;
2754}
2755
2756size_t audio_buffer_available(void)
2757{
2758 size_t size = 0;
2759 size_t core_size = core_available();
2760 if (audiobuf_handle > 0)
2761 return audiobuflen - AUDIO_BUFFER_RESERVE - 128;
2762 return MAX(core_size, size);
2750} 2763}
2751 2764
2752static void audio_reset_buffer_noalloc(void* buf, size_t bufsize) 2765static 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)
733} 733}
734 734
735static int audiobuf_handle; 735static int audiobuf_handle;
736#define AUDIO_BUFFER_RESERVE (256*1024)
736static size_t filebuflen; 737static size_t filebuflen;
737 738
739
740size_t audio_buffer_size(void)
741{
742 if (audiobuf_handle > 0)
743 return filebuflen - AUDIO_BUFFER_RESERVE;
744 return 0;
745}
746
738size_t audio_buffer_available(void) 747size_t audio_buffer_available(void)
739{ 748{
740 if (audiobuf_handle > 0) /* if allocated return what we got */ 749 size_t size = 0;
741 return filebuflen; 750 size_t core_size = core_available();
742 return core_available(); 751 if (audiobuf_handle > 0) /* if allocated return what we can give */
752 size = filebuflen - AUDIO_BUFFER_RESERVE - 128;
753 return MAX(core_size, size);
743} 754}
744 755
745/* Set up the audio buffer for playback 756/* 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
840 size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); 851 size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
841 ssize_t size = (ssize_t)old_size - wanted_size; 852 ssize_t size = (ssize_t)old_size - wanted_size;
842 /* keep at least 256K for the buffering */ 853 /* keep at least 256K for the buffering */
843 if ((size - extradata_size) < 256*1024) 854 if ((size - extradata_size) < AUDIO_BUFFER_RESERVE)
844 return BUFLIB_CB_CANNOT_SHRINK; 855 return BUFLIB_CB_CANNOT_SHRINK;
845 856
846 857
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)
92static union buflib_data* find_first_free(struct buflib_context *ctx); 93static union buflib_data* find_first_free(struct buflib_context *ctx);
93static union buflib_data* find_block_before(struct buflib_context *ctx, 94static 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 */ 618static size_t
602size_t 619free_space_at_end(struct buflib_context* ctx)
603buflib_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 */
635size_t
636buflib_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
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);
58void audio_next(void); 58void audio_next(void);
59void audio_prev(void); 59void audio_prev(void);
60int audio_status(void); 60int audio_status(void);
61/* size of the audio buffer */
62size_t audio_buffer_size(void);
63/* size of the buffer available for allocating memory from the audio buffer using core_*()
64 * returns core_available() if audio buffer is not allocated yet */
61size_t audio_buffer_available(void); 65size_t audio_buffer_available(void);
62void audio_ff_rewind(long newpos); 66void audio_ff_rewind(long newpos);
63void audio_flush_and_reload_tracks(void); 67void audio_flush_and_reload_tracks(void);