summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2011-08-30 14:01:45 +0000
committerThomas Martitz <kugel@rockbox.org>2011-08-30 14:01:45 +0000
commitbaa070cca6d459a7c5aed81f29e4cc4f6c7410b3 (patch)
tree5123360aea420b96e4a97a8e88cf51b4277152d9
parentd0b72e25903574acb1cf9184a6052cdd646dbc37 (diff)
downloadrockbox-baa070cca6d459a7c5aed81f29e4cc4f6c7410b3.tar.gz
rockbox-baa070cca6d459a7c5aed81f29e4cc4f6c7410b3.zip
GSoC/Buflib: Enable compaction in buflib.
This enables the ability to allocate (and free) memory dynamically without fragmentation, through compaction. This means allocations can move and fragmentation be reduced. Most changes are preparing Rockbox for this, which many times means adding a move callback which can temporarily disable movement when the corresponding code is in a critical section. For now, the audio buffer allocation has a central role, because it's the one having allocated most. This buffer is able to shrink itself, for which it needs to stop playback for a very short moment. For this, audio_buffer_available() returns the size of the audio buffer which can possibly be used by other allocations because the audio buffer can shrink. lastfm scrobbling and timestretch can now be toggled at runtime without requiring a reboot. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30381 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/debug_menu.c21
-rw-r--r--apps/dsp.c60
-rw-r--r--apps/filetree.c31
-rw-r--r--apps/filetypes.c22
-rw-r--r--apps/main.c6
-rw-r--r--apps/menus/playback_menu.c2
-rw-r--r--apps/mpeg.c121
-rw-r--r--apps/playback.c76
-rw-r--r--apps/playlist.c95
-rw-r--r--apps/playlist.h9
-rw-r--r--apps/plugin.c6
-rw-r--r--apps/plugin.h2
-rw-r--r--apps/plugins/imageviewer/imageviewer.c2
-rw-r--r--apps/plugins/mikmod/mikmod.c2
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c2
-rw-r--r--apps/plugins/rockpaint.c2
-rw-r--r--apps/scrobbler.c8
-rw-r--r--apps/tagcache.c144
-rw-r--r--apps/tagcache.h4
-rw-r--r--apps/tagtree.c229
-rw-r--r--apps/tagtree.h9
-rw-r--r--apps/tdspeed.c75
-rw-r--r--apps/tdspeed.h3
-rw-r--r--apps/tree.c78
-rw-r--r--apps/tree.h36
-rw-r--r--firmware/buflib.c5
-rw-r--r--firmware/common/dircache.c114
-rw-r--r--firmware/core_alloc.c1
28 files changed, 866 insertions, 299 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index fb8575ec62..6375094225 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -425,11 +425,32 @@ static const char* bf_getname(int selected_item, void *data,
425 return buffer; 425 return buffer;
426} 426}
427 427
428static int bf_action_cb(int action, struct gui_synclist* list)
429{
430 if (action == ACTION_STD_OK)
431 {
432 splash(HZ/1, "Attempting a 64k allocation");
433 int handle = core_alloc("test", 64<<10);
434 splash(HZ/2, (handle > 0) ? "Success":"Fail");
435 /* for some reason simplelist doesn't allow adding items here if
436 * info.get_name is given, so use normal list api */
437 gui_synclist_set_nb_items(list, core_get_num_blocks());
438 if (handle > 0)
439 core_free(handle);
440 action = ACTION_REDRAW;
441 }
442 else if (action == ACTION_NONE)
443 action = ACTION_REDRAW;
444 return action;
445}
446
428static bool dbg_buflib_allocs(void) 447static bool dbg_buflib_allocs(void)
429{ 448{
430 struct simplelist_info info; 449 struct simplelist_info info;
431 simplelist_info_init(&info, "mem allocs", core_get_num_blocks(), NULL); 450 simplelist_info_init(&info, "mem allocs", core_get_num_blocks(), NULL);
432 info.get_name = bf_getname; 451 info.get_name = bf_getname;
452 info.action_callback = bf_action_cb;
453 info.timeout = HZ/2;
433 return simplelist_show_list(&info); 454 return simplelist_show_list(&info);
434} 455}
435 456
diff --git a/apps/dsp.c b/apps/dsp.c
index a728dd75ea..167c043427 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -318,30 +318,50 @@ static void tdspeed_setup(struct dsp_config *dspc)
318 resample_buf = big_resample_buf; 318 resample_buf = big_resample_buf;
319} 319}
320 320
321
322static int move_callback(int handle, void* current, void* new)
323{
324 /* TODO */
325 (void)handle;(void)current;;
326 big_sample_buf = new;
327 return BUFLIB_CB_OK;
328}
329
330static struct buflib_callbacks ops = {
331 .move_callback = move_callback,
332 .shrink_callback = NULL,
333};
334
335
321void dsp_timestretch_enable(bool enabled) 336void dsp_timestretch_enable(bool enabled)
322{ 337{
323 /* Hook to set up timestretch buffer on first call to settings_apply() */ 338 /* Hook to set up timestretch buffer on first call to settings_apply() */
324 if (big_sample_buf_count < 0) /* Only do something on first call */ 339 static int handle;
340 if (enabled)
325 { 341 {
326 if (enabled) 342 if (big_sample_buf_count > 0)
327 { 343 return; /* already allocated and enabled */
328 int handle; 344 /* Set up timestretch buffers */
329 /* Set up timestretch buffers */ 345 big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO;
330 big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO; 346 big_sample_buf = small_resample_buf;
331 big_sample_buf = small_resample_buf; 347 handle = core_alloc_ex("resample buf",
332 handle = core_alloc("resample buf", 348 big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t), &ops);
333 big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t)); 349 if (handle > 0)
334 if (handle > 0) 350 { /* success, now setup tdspeed */
335 big_resample_buf = core_get_data(handle); 351 big_resample_buf = core_get_data(handle);
336 else 352 tdspeed_init();
337 big_sample_buf_count = 0; 353 tdspeed_setup(&AUDIO_DSP);
338 } 354 }
339 else 355 }
340 { 356 if (!enabled || (handle <= 0)) /* disable */
341 /* Not enabled at startup, "big" buffers will never be available */ 357 {
342 big_sample_buf_count = 0; 358 dsp_set_timestretch(PITCH_SPEED_100);
343 } 359 tdspeed_finish();
344 tdspeed_setup(&AUDIO_DSP); 360 if (handle > 0)
361 core_free(handle);
362 handle = 0;
363 big_sample_buf = NULL;
364 big_sample_buf_count = 0;
345 } 365 }
346} 366}
347 367
@@ -1211,7 +1231,7 @@ int dsp_callback(int msg, intptr_t param)
1211 */ 1231 */
1212int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count) 1232int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
1213{ 1233{
1214 int32_t *tmp[2]; 1234 static int32_t *tmp[2]; /* tdspeed_doit() needs it static */
1215 static long last_yield; 1235 static long last_yield;
1216 long tick; 1236 long tick;
1217 int written = 0; 1237 int written = 0;
diff --git a/apps/filetree.c b/apps/filetree.c
index 1aee80b6b2..35bb2a8fd0 100644
--- a/apps/filetree.c
+++ b/apps/filetree.c
@@ -29,6 +29,7 @@
29#include <limits.h> 29#include <limits.h>
30#include "bookmark.h" 30#include "bookmark.h"
31#include "tree.h" 31#include "tree.h"
32#include "core_alloc.h"
32#include "settings.h" 33#include "settings.h"
33#include "filetypes.h" 34#include "filetypes.h"
34#include "talk.h" 35#include "talk.h"
@@ -60,7 +61,8 @@ int ft_build_playlist(struct tree_context* c, int start_index)
60 int i; 61 int i;
61 int start=start_index; 62 int start=start_index;
62 63
63 struct entry *entries = c->cache.entries; 64 tree_lock_cache(c);
65 struct entry *entries = tree_get_entries(c);
64 66
65 for(i = 0;i < c->filesindir;i++) 67 for(i = 0;i < c->filesindir;i++)
66 { 68 {
@@ -77,6 +79,8 @@ int ft_build_playlist(struct tree_context* c, int start_index)
77 } 79 }
78 } 80 }
79 81
82 tree_unlock_cache(c);
83
80 return start_index; 84 return start_index;
81} 85}
82 86
@@ -127,13 +131,15 @@ static void check_file_thumbnails(struct tree_context* c)
127{ 131{
128 int i; 132 int i;
129 struct dirent *entry; 133 struct dirent *entry;
130 struct entry* entries = c->cache.entries; 134 struct entry* entries;
131 DIR *dir; 135 DIR *dir;
132 136
133 dir = opendir(c->currdir); 137 dir = opendir(c->currdir);
134 if(!dir) 138 if(!dir)
135 return; 139 return;
136 /* mark all files as non talking, except the .talk ones */ 140 /* mark all files as non talking, except the .talk ones */
141 entries = tree_get_entries(c);
142 tree_lock_cache(c);
137 for (i=0; i < c->filesindir; i++) 143 for (i=0; i < c->filesindir; i++)
138 { 144 {
139 if (entries[i].attr & ATTR_DIRECTORY) 145 if (entries[i].attr & ATTR_DIRECTORY)
@@ -177,6 +183,7 @@ static void check_file_thumbnails(struct tree_context* c)
177 } 183 }
178 } 184 }
179 } 185 }
186 tree_unlock_cache(c);
180 closedir(dir); 187 closedir(dir);
181} 188}
182 189
@@ -287,11 +294,11 @@ int ft_load(struct tree_context* c, const char* tempdir)
287 c->dirsindir = 0; 294 c->dirsindir = 0;
288 c->dirfull = false; 295 c->dirfull = false;
289 296
297 tree_lock_cache(c);
290 while ((entry = readdir(dir))) { 298 while ((entry = readdir(dir))) {
291 int len; 299 int len;
292 struct dirinfo info; 300 struct dirinfo info;
293 struct entry* table = c->cache.entries; 301 struct entry* dptr = tree_get_entry_at(c, files_in_dir);
294 struct entry* dptr = &table[files_in_dir];
295 if (!entry) 302 if (!entry)
296 break; 303 break;
297 304
@@ -369,7 +376,7 @@ int ft_load(struct tree_context* c, const char* tempdir)
369 376
370 ++files_in_dir; 377 ++files_in_dir;
371 378
372 dptr->name = &c->cache.name_buffer[name_buffer_used]; 379 dptr->name = core_get_data(c->cache.name_buffer_handle)+name_buffer_used;
373 dptr->time_write = 380 dptr->time_write =
374 (long)info.wrtdate<<16 | 381 (long)info.wrtdate<<16 |
375 (long)info.wrttime; /* in one # */ 382 (long)info.wrttime; /* in one # */
@@ -384,13 +391,14 @@ int ft_load(struct tree_context* c, const char* tempdir)
384 closedir(dir); 391 closedir(dir);
385 392
386 compare_sort_dir = c->sort_dir; 393 compare_sort_dir = c->sort_dir;
387 qsort(c->cache.entries, files_in_dir, sizeof(struct entry), compare); 394 qsort(tree_get_entries(c), files_in_dir, sizeof(struct entry), compare);
388 395
389 /* If thumbnail talking is enabled, make an extra run to mark files with 396 /* If thumbnail talking is enabled, make an extra run to mark files with
390 associated thumbnails, so we don't do unsuccessful spinups later. */ 397 associated thumbnails, so we don't do unsuccessful spinups later. */
391 if (global_settings.talk_file_clip) 398 if (global_settings.talk_file_clip)
392 check_file_thumbnails(c); /* map .talk to ours */ 399 check_file_thumbnails(c); /* map .talk to ours */
393 400
401 tree_unlock_cache(c);
394 return 0; 402 return 0;
395} 403}
396#ifdef HAVE_LCD_BITMAP 404#ifdef HAVE_LCD_BITMAP
@@ -424,15 +432,15 @@ int ft_enter(struct tree_context* c)
424{ 432{
425 int rc = GO_TO_PREVIOUS; 433 int rc = GO_TO_PREVIOUS;
426 char buf[MAX_PATH]; 434 char buf[MAX_PATH];
427 struct entry* table = c->cache.entries; 435 struct entry* file = tree_get_entry_at(c, c->selected_item);
428 struct entry *file = &table[c->selected_item]; 436 int file_attr = file->attr;
429 437
430 if (c->currdir[1]) 438 if (c->currdir[1])
431 snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name); 439 snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name);
432 else 440 else
433 snprintf(buf,sizeof(buf),"/%s",file->name); 441 snprintf(buf,sizeof(buf),"/%s",file->name);
434 442
435 if (file->attr & ATTR_DIRECTORY) { 443 if (file_attr & ATTR_DIRECTORY) {
436 memcpy(c->currdir, buf, sizeof(c->currdir)); 444 memcpy(c->currdir, buf, sizeof(c->currdir));
437 if ( c->dirlevel < MAX_DIR_LEVELS ) 445 if ( c->dirlevel < MAX_DIR_LEVELS )
438 c->selected_item_history[c->dirlevel] = c->selected_item; 446 c->selected_item_history[c->dirlevel] = c->selected_item;
@@ -444,7 +452,7 @@ int ft_enter(struct tree_context* c)
444 bool play = false; 452 bool play = false;
445 int start_index=0; 453 int start_index=0;
446 454
447 switch ( file->attr & FILE_ATTR_MASK ) { 455 switch ( file_attr & FILE_ATTR_MASK ) {
448 case FILE_ATTR_M3U: 456 case FILE_ATTR_M3U:
449 if (!bookmark_autoload(buf)) 457 if (!bookmark_autoload(buf))
450 playlist_viewer_ex(buf); 458 playlist_viewer_ex(buf);
@@ -612,7 +620,7 @@ int ft_enter(struct tree_context* c)
612 char *plugin = buf, *argument = NULL, lua_path[MAX_PATH]; 620 char *plugin = buf, *argument = NULL, lua_path[MAX_PATH];
613 int ret; 621 int ret;
614 622
615 if ((file->attr & FILE_ATTR_MASK) == FILE_ATTR_LUA) { 623 if ((file_attr & FILE_ATTR_MASK) == FILE_ATTR_LUA) {
616 snprintf(lua_path, sizeof(lua_path)-1, "%s/lua.rock", VIEWERS_DIR); /* Use a #define here ? */ 624 snprintf(lua_path, sizeof(lua_path)-1, "%s/lua.rock", VIEWERS_DIR); /* Use a #define here ? */
617 plugin = lua_path; 625 plugin = lua_path;
618 argument = buf; 626 argument = buf;
@@ -658,6 +666,7 @@ int ft_enter(struct tree_context* c)
658 break; 666 break;
659 } 667 }
660 668
669 struct entry* file = tree_get_entry_at(c, c->selected_item);
661 plugin = filetype_get_plugin(file); 670 plugin = filetype_get_plugin(file);
662 if (plugin) 671 if (plugin)
663 { 672 {
diff --git a/apps/filetypes.c b/apps/filetypes.c
index c52c734a1d..942ff329fe 100644
--- a/apps/filetypes.c
+++ b/apps/filetypes.c
@@ -184,6 +184,26 @@ static unsigned char highest_attr = 0;
184static int viewer_count = 0; 184static int viewer_count = 0;
185 185
186static int strdup_handle, strdup_bufsize, strdup_cur_idx; 186static int strdup_handle, strdup_bufsize, strdup_cur_idx;
187static int move_callback(int handle, void* current, void* new)
188{
189 /*could compare to strdup_handle, but ops is only used once */
190 (void)handle;
191 size_t diff = new - current;
192#define FIX_PTR(x) \
193 { if ((void*)x > current && (void*)x < (current+strdup_bufsize)) x+= diff; }
194 for(int i = 0; i < filetype_count; i++)
195 {
196 FIX_PTR(filetypes[i].extension);
197 FIX_PTR(filetypes[i].plugin);
198 }
199 return BUFLIB_CB_OK;
200}
201
202static struct buflib_callbacks ops = {
203 .move_callback = move_callback,
204 .shrink_callback = NULL,
205};
206
187static char *filetypes_strdup(char* string) 207static char *filetypes_strdup(char* string)
188{ 208{
189 char *buffer = core_get_data(strdup_handle) + strdup_cur_idx; 209 char *buffer = core_get_data(strdup_handle) + strdup_cur_idx;
@@ -323,7 +343,7 @@ void filetype_init(void)
323 return; 343 return;
324 344
325 strdup_bufsize = filesize(fd); 345 strdup_bufsize = filesize(fd);
326 strdup_handle = core_alloc("filetypes", strdup_bufsize); 346 strdup_handle = core_alloc_ex("filetypes", strdup_bufsize, &ops);
327 if (strdup_handle <= 0) 347 if (strdup_handle <= 0)
328 return; 348 return;
329 read_builtin_types(); 349 read_builtin_types();
diff --git a/apps/main.c b/apps/main.c
index cc9c9e8d8e..07a8bba44c 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -403,9 +403,6 @@ static void init(void)
403#endif /* CONFIG_CODEC != SWCODEC */ 403#endif /* CONFIG_CODEC != SWCODEC */
404 404
405 scrobbler_init(); 405 scrobbler_init();
406#if CONFIG_CODEC == SWCODEC && defined (HAVE_PITCHSCREEN)
407 tdspeed_init();
408#endif /* CONFIG_CODEC == SWCODEC */
409 406
410 audio_init(); 407 audio_init();
411 408
@@ -659,9 +656,6 @@ static void init(void)
659 tree_mem_init(); 656 tree_mem_init();
660 filetype_init(); 657 filetype_init();
661 scrobbler_init(); 658 scrobbler_init();
662#if CONFIG_CODEC == SWCODEC && defined (HAVE_PITCHSCREEN)
663 tdspeed_init();
664#endif /* CONFIG_CODEC == SWCODEC */
665 theme_init_buffer(); 659 theme_init_buffer();
666 660
667#if CONFIG_CODEC != SWCODEC 661#if CONFIG_CODEC != SWCODEC
diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c
index 1b1a13a6a5..a219373a8b 100644
--- a/apps/menus/playback_menu.c
+++ b/apps/menus/playback_menu.c
@@ -142,7 +142,7 @@ static int audioscrobbler_callback(int action,const struct menu_item_ex *this_it
142 { 142 {
143 case ACTION_EXIT_MENUITEM: /* on exit */ 143 case ACTION_EXIT_MENUITEM: /* on exit */
144 if (!scrobbler_is_enabled() && global_settings.audioscrobbler) 144 if (!scrobbler_is_enabled() && global_settings.audioscrobbler)
145 splash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); 145 scrobbler_init();
146 146
147 if(scrobbler_is_enabled() && !global_settings.audioscrobbler) 147 if(scrobbler_is_enabled() && !global_settings.audioscrobbler)
148 scrobbler_shutdown(); 148 scrobbler_shutdown();
diff --git a/apps/mpeg.c b/apps/mpeg.c
index 595f943545..0b1413e195 100644
--- a/apps/mpeg.c
+++ b/apps/mpeg.c
@@ -39,7 +39,7 @@
39#include "mp3_playback.h" 39#include "mp3_playback.h"
40#include "talk.h" 40#include "talk.h"
41#include "sound.h" 41#include "sound.h"
42#include "bitswap.h" 42#include "system.h"
43#include "appevents.h" 43#include "appevents.h"
44#include "playlist.h" 44#include "playlist.h"
45#include "cuesheet.h" 45#include "cuesheet.h"
@@ -140,6 +140,7 @@ static struct cuesheet *curr_cuesheet = NULL;
140static bool checked_for_cuesheet = false; 140static bool checked_for_cuesheet = false;
141 141
142static const char mpeg_thread_name[] = "mpeg"; 142static const char mpeg_thread_name[] = "mpeg";
143static unsigned int audio_thread_id;
143static unsigned int mpeg_errno; 144static unsigned int mpeg_errno;
144 145
145static bool playing = false; /* We are playing an MP3 stream */ 146static bool playing = false; /* We are playing an MP3 stream */
@@ -492,20 +493,81 @@ unsigned long mpeg_get_last_header(void)
492#endif /* !SIMULATOR */ 493#endif /* !SIMULATOR */
493} 494}
494 495
495/* Buffer must not move. And not shrink for now */ 496static void do_stop(void)
496static struct buflib_callbacks ops = { NULL, NULL }; 497{
498 is_playing = false;
499 paused = false;
500
501#ifndef SIMULATOR
502 if (playing)
503 playlist_update_resume_info(audio_current_track());
504
505 stop_playing();
506 mpeg_stop_done = true;
507#else
508 playing = false;
509#endif
510}
511
512static void audio_reset_buffer_noalloc(void* buf, size_t bufsize);
513/* Buffer must not move. */
514static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size)
515{
516 long offset = audio_current_track()->offset;
517 int status = audio_status();
518 /* TODO: Do it without stopping playback, if possible */
519 /* don't call audio_hard_stop() as it frees this handle */
520 if (thread_self() == audio_thread_id)
521 { /* inline case MPEG_STOP (audio_stop()) response
522 * if we're in the audio thread since audio_stop() otherwise deadlocks */
523 do_stop();
524 }
525 else
526 audio_stop();
527 talk_buffer_steal(); /* we obtain control over the buffer */
528
529 /* we should be free to change the buffer now */
530 size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
531 ssize_t size = (ssize_t)old_size - wanted_size;
532 switch (hints & BUFLIB_SHRINK_POS_MASK)
533 {
534 case BUFLIB_SHRINK_POS_BACK:
535 core_shrink(handle, start, size);
536 audio_reset_buffer_noalloc(start, size);
537 break;
538 case BUFLIB_SHRINK_POS_FRONT:
539 core_shrink(handle, start + wanted_size, size);
540 audio_reset_buffer_noalloc(start + wanted_size, size);
541 break;
542 }
543 if (!(status & AUDIO_STATUS_PAUSE))
544 { /* safe to call even from the audio thread (due to queue_post()) */
545 audio_play(offset);
546 }
547
548 return BUFLIB_CB_OK;
549}
550
551static struct buflib_callbacks ops = {
552 .move_callback = NULL,
553 .shrink_callback = shrink_callback,
554};
555
497unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) 556unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
498{ 557{
499 (void)talk_buf; /* always grab the voice buffer for now */ 558 (void)talk_buf; /* always grab the voice buffer for now */
500 559
501 if (buffer_size) /* special case for talk_init() */ 560 if (buffer_size) /* special case for talk_init() */
561 audio_hard_stop();
562
563 if (!audiobuf_handle)
502 { 564 {
503 size_t bufsize; 565 size_t bufsize;
504 audio_hard_stop();
505 /* audio_hard_stop() frees audiobuf, so re-aquire */ 566 /* audio_hard_stop() frees audiobuf, so re-aquire */
506 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); 567 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
507 audiobuflen = bufsize; 568 audiobuflen = bufsize;
508 *buffer_size = audiobuflen; 569 if (buffer_size)
570 *buffer_size = audiobuflen;
509 } 571 }
510 mpeg_audiobuf = core_get_data(audiobuf_handle); 572 mpeg_audiobuf = core_get_data(audiobuf_handle);
511 573
@@ -1314,15 +1376,7 @@ static void mpeg_thread(void)
1314 break; 1376 break;
1315 1377
1316 case MPEG_STOP: 1378 case MPEG_STOP:
1317 DEBUGF("MPEG_STOP\n"); 1379 do_stop();
1318 is_playing = false;
1319 paused = false;
1320
1321 if (playing)
1322 playlist_update_resume_info(audio_current_track());
1323
1324 stop_playing();
1325 mpeg_stop_done = true;
1326 break; 1380 break;
1327 1381
1328 case MPEG_PAUSE: 1382 case MPEG_PAUSE:
@@ -2679,19 +2733,29 @@ size_t audio_buffer_available(void)
2679 return core_available(); 2733 return core_available();
2680} 2734}
2681 2735
2682static void audio_reset_buffer(void) 2736static void audio_reset_buffer_noalloc(void* buf, size_t bufsize)
2683{ 2737{
2684 talk_buffer_steal(); /* will use the mp3 buffer */ 2738 talk_buffer_steal(); /* will use the mp3 buffer */
2739 mpeg_audiobuf = buf;
2740 audiobuflen = bufsize;
2741 if (global_settings.cuesheet)
2742 { /* enable cuesheet support */
2743 curr_cuesheet = (struct cuesheet*)mpeg_audiobuf;
2744 mpeg_audiobuf = SKIPBYTES(mpeg_audiobuf, sizeof(struct cuesheet));
2745 audiobuflen -= sizeof(struct cuesheet);
2746 }
2747 talkbuf_init(mpeg_audiobuf);
2748}
2749
2750static void audio_reset_buffer(void)
2751{
2752 size_t bufsize = audiobuflen;
2685 2753
2686 /* alloc buffer if it's was never allocated or freed by audio_hard_stop() */ 2754 /* alloc buffer if it's was never allocated or freed by audio_hard_stop() */
2687 if (!audiobuf_handle) 2755 if (!audiobuf_handle)
2688 {
2689 size_t bufsize; /* dont break strict-aliasing */
2690 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); 2756 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
2691 mpeg_audiobuf = core_get_data(audiobuf_handle); 2757
2692 audiobuflen = bufsize; 2758 audio_reset_buffer_noalloc(core_get_data(audiobuf_handle), bufsize);
2693 }
2694 talkbuf_init(mpeg_audiobuf);
2695} 2759}
2696 2760
2697void audio_play(long offset) 2761void audio_play(long offset)
@@ -2923,13 +2987,6 @@ static void mpeg_thread(void)
2923void audio_init(void) 2987void audio_init(void)
2924{ 2988{
2925 mpeg_errno = 0; 2989 mpeg_errno = 0;
2926 /* cuesheet support */
2927 if (global_settings.cuesheet)
2928 {
2929 int handle = core_alloc("cuesheet", sizeof(struct cuesheet));
2930 if (handle > 0)
2931 curr_cuesheet = core_get_data(handle);
2932 }
2933 2990
2934 talk_init(); 2991 talk_init();
2935 audio_reset_buffer(); 2992 audio_reset_buffer();
@@ -2937,10 +2994,10 @@ void audio_init(void)
2937#ifndef SIMULATOR 2994#ifndef SIMULATOR
2938 queue_init(&mpeg_queue, true); 2995 queue_init(&mpeg_queue, true);
2939#endif /* !SIMULATOR */ 2996#endif /* !SIMULATOR */
2940 create_thread(mpeg_thread, mpeg_stack, 2997 audio_thread_id = create_thread(mpeg_thread, mpeg_stack,
2941 sizeof(mpeg_stack), 0, mpeg_thread_name 2998 sizeof(mpeg_stack), 0, mpeg_thread_name
2942 IF_PRIO(, PRIORITY_SYSTEM) 2999 IF_PRIO(, PRIORITY_SYSTEM)
2943 IF_COP(, CPU)); 3000 IF_COP(, CPU));
2944 3001
2945 memset(trackdata, 0, sizeof(trackdata)); 3002 memset(trackdata, 0, sizeof(trackdata));
2946 3003
diff --git a/apps/playback.c b/apps/playback.c
index e35d652ffb..af077e639a 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -732,8 +732,6 @@ static void scratch_mem_init(void *mem)
732 } 732 }
733} 733}
734 734
735/* Buffer must not move. And not shrink for now */
736static struct buflib_callbacks ops = { NULL, NULL };
737static int audiobuf_handle; 735static int audiobuf_handle;
738static size_t filebuflen; 736static size_t filebuflen;
739 737
@@ -744,8 +742,9 @@ size_t audio_buffer_available(void)
744 return core_available(); 742 return core_available();
745} 743}
746 744
747/* Set up the audio buffer for playback */ 745/* Set up the audio buffer for playback
748static void audio_reset_buffer(void) 746 * filebuflen must be pre-initialized with the maximum size */
747static void audio_reset_buffer_noalloc(void* filebuf)
749{ 748{
750 /* 749 /*
751 * Layout audio buffer as follows: 750 * Layout audio buffer as follows:
@@ -761,11 +760,6 @@ static void audio_reset_buffer(void)
761 760
762 /* Initially set up file buffer as all space available */ 761 /* Initially set up file buffer as all space available */
763 size_t allocsize; 762 size_t allocsize;
764 if (audiobuf_handle > 0)
765 audiobuf_handle = core_free(audiobuf_handle);
766
767 audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
768 unsigned char *filebuf = core_get_data(audiobuf_handle);
769 763
770 /* Subtract whatever voice needs */ 764 /* Subtract whatever voice needs */
771 allocsize = talkbuf_init(filebuf); 765 allocsize = talkbuf_init(filebuf);
@@ -830,6 +824,70 @@ bufpanic:
830 panicf("%s(): EOM (%zu > %zu)", __func__, allocsize, filebuflen); 824 panicf("%s(): EOM (%zu > %zu)", __func__, allocsize, filebuflen);
831} 825}
832 826
827
828/* Buffer must not move. */
829static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size)
830{
831 long offset = audio_current_track()->offset;
832 int status = audio_status();
833 /* TODO: Do it without stopping playback, if possible */
834 /* don't call audio_hard_stop() as it frees this handle */
835 if (thread_self() == audio_thread_id)
836 { /* inline case Q_AUDIO_STOP (audio_hard_stop() response
837 * if we're in the audio thread */
838 audio_stop_playback();
839 queue_clear(&audio_queue);
840 }
841 else
842 audio_queue_send(Q_AUDIO_STOP, 1);
843#ifdef PLAYBACK_VOICE
844 voice_stop();
845#endif
846 /* we should be free to change the buffer now */
847 size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
848 ssize_t size = (ssize_t)old_size - wanted_size;
849 /* set final buffer size before calling audio_reset_buffer_noalloc() */
850 filebuflen = size;
851 switch (hints & BUFLIB_SHRINK_POS_MASK)
852 {
853 case BUFLIB_SHRINK_POS_BACK:
854 core_shrink(handle, start, size);
855 audio_reset_buffer_noalloc(start);
856 break;
857 case BUFLIB_SHRINK_POS_FRONT:
858 core_shrink(handle, start + wanted_size, size);
859 audio_reset_buffer_noalloc(start + wanted_size);
860 break;
861 }
862 if ((status & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY)
863 {
864 if (thread_self() == audio_thread_id)
865 audio_start_playback(offset, 0); /* inline Q_AUDIO_PLAY */
866 else
867 audio_play(offset);
868 }
869
870 return BUFLIB_CB_OK;
871}
872
873static struct buflib_callbacks ops = {
874 .move_callback = NULL,
875 .shrink_callback = shrink_callback,
876};
877
878static void audio_reset_buffer(void)
879{
880 if (audiobuf_handle > 0)
881 {
882 core_free(audiobuf_handle);
883 audiobuf_handle = 0;
884 }
885 audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
886 unsigned char *filebuf = core_get_data(audiobuf_handle);
887
888 audio_reset_buffer_noalloc(filebuf);
889}
890
833/* Set the buffer margin to begin rebuffering when 'seconds' from empty */ 891/* Set the buffer margin to begin rebuffering when 'seconds' from empty */
834static void audio_update_filebuf_watermark(int seconds) 892static void audio_update_filebuf_watermark(int seconds)
835{ 893{
diff --git a/apps/playlist.c b/apps/playlist.c
index 77d8141af8..f6dda977f4 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -993,14 +993,14 @@ static int sort_playlist(struct playlist_info* playlist, bool start_current,
993 unsigned int current = playlist->indices[playlist->index]; 993 unsigned int current = playlist->indices[playlist->index];
994 994
995 if (playlist->amount > 0) 995 if (playlist->amount > 0)
996 qsort(playlist->indices, playlist->amount, 996 qsort((void*)playlist->indices, playlist->amount,
997 sizeof(playlist->indices[0]), compare); 997 sizeof(playlist->indices[0]), compare);
998 998
999#ifdef HAVE_DIRCACHE 999#ifdef HAVE_DIRCACHE
1000 /** We need to re-check the song names from disk because qsort can't 1000 /** We need to re-check the song names from disk because qsort can't
1001 * sort two arrays at once :/ 1001 * sort two arrays at once :/
1002 * FIXME: Please implement a better way to do this. */ 1002 * FIXME: Please implement a better way to do this. */
1003 memset(playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int)); 1003 memset((void*)playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int));
1004 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0); 1004 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
1005#endif 1005#endif
1006 1006
@@ -1378,7 +1378,7 @@ static int get_filename(struct playlist_info* playlist, int index, int seek,
1378 1378
1379 if (playlist->in_ram && !control_file && max < 0) 1379 if (playlist->in_ram && !control_file && max < 0)
1380 { 1380 {
1381 max = strlcpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf)); 1381 max = strlcpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf));
1382 } 1382 }
1383 else if (max < 0) 1383 else if (max < 0)
1384 { 1384 {
@@ -1534,9 +1534,10 @@ static int get_next_dir(char *dir, bool is_forward, bool recursion)
1534 break; 1534 break;
1535 } 1535 }
1536 1536
1537 files = tc->cache.entries; 1537 files = tree_get_entries(tc);
1538 num_files = tc->filesindir; 1538 num_files = tc->filesindir;
1539 1539
1540 tree_lock_cache(tc);
1540 for (i=0; i<num_files; i++) 1541 for (i=0; i<num_files; i++)
1541 { 1542 {
1542 /* user abort */ 1543 /* user abort */
@@ -1562,6 +1563,7 @@ static int get_next_dir(char *dir, bool is_forward, bool recursion)
1562 start_dir = NULL; 1563 start_dir = NULL;
1563 } 1564 }
1564 } 1565 }
1566 tree_unlock_cache(tc);
1565 1567
1566 if (!exit) 1568 if (!exit)
1567 { 1569 {
@@ -1612,7 +1614,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1612 return -2; 1614 return -2;
1613 } 1615 }
1614 1616
1615 files = tc->cache.entries; 1617 files = tree_get_entries(tc);
1616 num_files = tc->filesindir; 1618 num_files = tc->filesindir;
1617 1619
1618 for (i=0; i<num_files; i++) 1620 for (i=0; i<num_files; i++)
@@ -1629,6 +1631,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1629 if (has_music) 1631 if (has_music)
1630 return 0; 1632 return 0;
1631 1633
1634 tree_lock_cache(tc);
1632 if (has_subdir && recurse) 1635 if (has_subdir && recurse)
1633 { 1636 {
1634 for (i=0; i<num_files; i++) 1637 for (i=0; i<num_files; i++)
@@ -1647,6 +1650,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1647 } 1650 }
1648 } 1651 }
1649 } 1652 }
1653 tree_unlock_cache(tc);
1650 1654
1651 if (result < 0) 1655 if (result < 0)
1652 { 1656 {
@@ -1925,6 +1929,31 @@ static int rotate_index(const struct playlist_info* playlist, int index)
1925} 1929}
1926 1930
1927/* 1931/*
1932 * Need no movement protection since all 3 allocations are not passed to
1933 * other functions which can yield().
1934 */
1935static int move_callback(int handle, void* current, void* new)
1936{
1937 (void)handle;
1938 struct playlist_info* playlist = &current_playlist;
1939 if (current == playlist->indices)
1940 playlist->indices = new;
1941 else if (current == playlist->filenames)
1942 playlist->filenames = new;
1943 /* buffer can possibly point to a new buffer temporarily (playlist_save()).
1944 * just don't overwrite the pointer to that temp buffer */
1945 else if (current == playlist->buffer)
1946 playlist->buffer = new;
1947
1948 return BUFLIB_CB_OK;
1949}
1950
1951
1952static struct buflib_callbacks ops = {
1953 .move_callback = move_callback,
1954 .shrink_callback = NULL,
1955};
1956/*
1928 * Initialize playlist entries at startup 1957 * Initialize playlist entries at startup
1929 */ 1958 */
1930void playlist_init(void) 1959void playlist_init(void)
@@ -1941,20 +1970,23 @@ void playlist_init(void)
1941 playlist->fd = -1; 1970 playlist->fd = -1;
1942 playlist->control_fd = -1; 1971 playlist->control_fd = -1;
1943 playlist->max_playlist_size = global_settings.max_files_in_playlist; 1972 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1944 handle = core_alloc("playlist idx", playlist->max_playlist_size * sizeof(int)); 1973 handle = core_alloc_ex("playlist idx",
1974 playlist->max_playlist_size * sizeof(int), &ops);
1945 playlist->indices = core_get_data(handle); 1975 playlist->indices = core_get_data(handle);
1946 playlist->buffer_size = 1976 playlist->buffer_size =
1947 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; 1977 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1948 handle = core_alloc("playlist buf", playlist->buffer_size); 1978 handle = core_alloc_ex("playlist buf",
1979 playlist->buffer_size, &ops);
1949 playlist->buffer = core_get_data(handle); 1980 playlist->buffer = core_get_data(handle);
1950 playlist->control_mutex = &current_playlist_mutex; 1981 playlist->control_mutex = &current_playlist_mutex;
1951 1982
1952 empty_playlist(playlist, true); 1983 empty_playlist(playlist, true);
1953 1984
1954#ifdef HAVE_DIRCACHE 1985#ifdef HAVE_DIRCACHE
1955 handle = core_alloc("playlist dc", playlist->max_playlist_size * sizeof(int)); 1986 handle = core_alloc_ex("playlist dc",
1987 playlist->max_playlist_size * sizeof(int), &ops);
1956 playlist->filenames = core_get_data(handle); 1988 playlist->filenames = core_get_data(handle);
1957 memset(playlist->filenames, 0xff, 1989 memset((void*)playlist->filenames, 0xff,
1958 playlist->max_playlist_size * sizeof(int)); 1990 playlist->max_playlist_size * sizeof(int));
1959 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack), 1991 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1960 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND) 1992 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
@@ -2404,7 +2436,7 @@ int playlist_add(const char *filename)
2404#endif 2436#endif
2405 playlist->amount++; 2437 playlist->amount++;
2406 2438
2407 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename); 2439 strcpy((char*)&playlist->buffer[playlist->buffer_end_pos], filename);
2408 playlist->buffer_end_pos += len; 2440 playlist->buffer_end_pos += len;
2409 playlist->buffer[playlist->buffer_end_pos++] = '\0'; 2441 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2410 2442
@@ -2731,6 +2763,7 @@ int playlist_create_ex(struct playlist_info* playlist,
2731 } 2763 }
2732 2764
2733 playlist->buffer_size = 0; 2765 playlist->buffer_size = 0;
2766 playlist->buffer_handle = -1;
2734 playlist->buffer = NULL; 2767 playlist->buffer = NULL;
2735 playlist->control_mutex = &created_playlist_mutex; 2768 playlist->control_mutex = &created_playlist_mutex;
2736 } 2769 }
@@ -2779,10 +2812,10 @@ int playlist_set_current(struct playlist_info* playlist)
2779 2812
2780 if (playlist->indices && playlist->indices != current_playlist.indices) 2813 if (playlist->indices && playlist->indices != current_playlist.indices)
2781 { 2814 {
2782 memcpy(current_playlist.indices, playlist->indices, 2815 memcpy((void*)current_playlist.indices, (void*)playlist->indices,
2783 playlist->max_playlist_size*sizeof(int)); 2816 playlist->max_playlist_size*sizeof(int));
2784#ifdef HAVE_DIRCACHE 2817#ifdef HAVE_DIRCACHE
2785 memcpy(current_playlist.filenames, playlist->filenames, 2818 memcpy((void*)current_playlist.filenames, (void*)playlist->filenames,
2786 playlist->max_playlist_size*sizeof(int)); 2819 playlist->max_playlist_size*sizeof(int));
2787#endif 2820#endif
2788 } 2821 }
@@ -3358,6 +3391,7 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3358 char tmp_buf[MAX_PATH+1]; 3391 char tmp_buf[MAX_PATH+1];
3359 int result = 0; 3392 int result = 0;
3360 bool overwrite_current = false; 3393 bool overwrite_current = false;
3394 int old_handle = -1;
3361 char* old_buffer = NULL; 3395 char* old_buffer = NULL;
3362 size_t old_buffer_size = 0; 3396 size_t old_buffer_size = 0;
3363 3397
@@ -3380,15 +3414,16 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3380 { 3414 {
3381 /* not enough buffer space to store updated indices */ 3415 /* not enough buffer space to store updated indices */
3382 /* Try to get a buffer */ 3416 /* Try to get a buffer */
3383 old_buffer = playlist->buffer; 3417 old_handle = playlist->buffer_handle;
3418 /* can ignore volatile here, because core_get_data() is called later */
3419 old_buffer = (char*)playlist->buffer;
3384 old_buffer_size = playlist->buffer_size; 3420 old_buffer_size = playlist->buffer_size;
3385 playlist->buffer = plugin_get_buffer((size_t*)&playlist->buffer_size); 3421 playlist->buffer = plugin_get_buffer((size_t*)&playlist->buffer_size);
3386 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int))) 3422 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3387 { 3423 {
3388 playlist->buffer = old_buffer;
3389 playlist->buffer_size = old_buffer_size;
3390 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); 3424 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3391 return -1; 3425 result = -1;
3426 goto reset_old_buffer;
3392 } 3427 }
3393 } 3428 }
3394 3429
@@ -3409,12 +3444,8 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3409 if (fd < 0) 3444 if (fd < 0)
3410 { 3445 {
3411 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); 3446 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3412 if (old_buffer != NULL) 3447 result = -1;
3413 { 3448 goto reset_old_buffer;
3414 playlist->buffer = old_buffer;
3415 playlist->buffer_size = old_buffer_size;
3416 }
3417 return -1;
3418 } 3449 }
3419 3450
3420 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false); 3451 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
@@ -3514,11 +3545,12 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3514 } 3545 }
3515 3546
3516 cpu_boost(false); 3547 cpu_boost(false);
3517 if (old_buffer != NULL) 3548
3518 { 3549reset_old_buffer:
3519 playlist->buffer = old_buffer; 3550 if (old_handle > 0)
3520 playlist->buffer_size = old_buffer_size; 3551 old_buffer = core_get_data(old_handle);
3521 } 3552 playlist->buffer = old_buffer;
3553 playlist->buffer_size = old_buffer_size;
3522 3554
3523 return result; 3555 return result;
3524} 3556}
@@ -3534,9 +3566,9 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
3534 char buf[MAX_PATH+1]; 3566 char buf[MAX_PATH+1];
3535 int result = 0; 3567 int result = 0;
3536 int num_files = 0; 3568 int num_files = 0;
3537 int i; 3569 int i;;
3538 struct entry *files;
3539 struct tree_context* tc = tree_get_context(); 3570 struct tree_context* tc = tree_get_context();
3571 struct tree_cache* cache = &tc->cache;
3540 int old_dirfilter = *(tc->dirfilter); 3572 int old_dirfilter = *(tc->dirfilter);
3541 3573
3542 if (!callback) 3574 if (!callback)
@@ -3552,7 +3584,6 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
3552 return -1; 3584 return -1;
3553 } 3585 }
3554 3586
3555 files = tc->cache.entries;
3556 num_files = tc->filesindir; 3587 num_files = tc->filesindir;
3557 3588
3558 /* we've overwritten the dircache so tree browser will need to be 3589 /* we've overwritten the dircache so tree browser will need to be
@@ -3568,6 +3599,7 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
3568 break; 3599 break;
3569 } 3600 }
3570 3601
3602 struct entry *files = core_get_data(cache->entries_handle);
3571 if (files[i].attr & ATTR_DIRECTORY) 3603 if (files[i].attr & ATTR_DIRECTORY)
3572 { 3604 {
3573 if (recurse) 3605 if (recurse)
@@ -3586,8 +3618,7 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
3586 result = -1; 3618 result = -1;
3587 break; 3619 break;
3588 } 3620 }
3589 3621
3590 files = tc->cache.entries;
3591 num_files = tc->filesindir; 3622 num_files = tc->filesindir;
3592 if (!num_files) 3623 if (!num_files)
3593 { 3624 {
diff --git a/apps/playlist.h b/apps/playlist.h
index f14b5c6460..6dd5535df1 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -80,15 +80,16 @@ struct playlist_info
80 int control_fd; /* descriptor of the open control file */ 80 int control_fd; /* descriptor of the open control file */
81 bool control_created; /* has control file been created? */ 81 bool control_created; /* has control file been created? */
82 int dirlen; /* Length of the path to the playlist file */ 82 int dirlen; /* Length of the path to the playlist file */
83 unsigned long *indices; /* array of indices */ 83 volatile unsigned long *indices; /* array of indices */
84 int *filenames; /* Array of dircache indices */ 84 volatile int *filenames; /* Array of dircache indices */
85 int max_playlist_size; /* Max number of files in playlist. Mirror of 85 int max_playlist_size; /* Max number of files in playlist. Mirror of
86 global_settings.max_files_in_playlist */ 86 global_settings.max_files_in_playlist */
87 bool in_ram; /* playlist stored in ram (dirplay) */ 87 bool in_ram; /* playlist stored in ram (dirplay) */
88 int buffer_handle; /* handle to the below buffer (-1 if non-buflib) */
88 89
89 union { 90 union {
90 char *buffer; /* buffer for in-ram playlists */ 91 volatile char *buffer;/* buffer for in-ram playlists */
91 int *seek_buf; /* buffer for seeks in real playlists */ 92 int *seek_buf; /* buffer for seeks in real playlists */
92 }; 93 };
93 int buffer_size; /* size of buffer */ 94 int buffer_size; /* size of buffer */
94 int buffer_end_pos; /* last position where buffer was written */ 95 int buffer_end_pos; /* last position where buffer was written */
diff --git a/apps/plugin.c b/apps/plugin.c
index 32b77ad287..43d9e03acf 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -789,6 +789,8 @@ static const struct plugin_api rockbox_api = {
789 789
790 /* new stuff at the end, sort into place next time 790 /* new stuff at the end, sort into place next time
791 the API gets incompatible */ 791 the API gets incompatible */
792 tree_get_entries,
793 tree_get_entry_at,
792}; 794};
793 795
794int plugin_load(const char* plugin, const void* parameter) 796int plugin_load(const char* plugin, const void* parameter)
@@ -865,6 +867,9 @@ int plugin_load(const char* plugin, const void* parameter)
865 lcd_remote_update(); 867 lcd_remote_update();
866#endif 868#endif
867 push_current_activity(ACTIVITY_PLUGIN); 869 push_current_activity(ACTIVITY_PLUGIN);
870 /* some plugins assume the entry cache doesn't move and save pointers to it
871 * they should be fixed properly instead of this lock */
872 tree_lock_cache(tree_get_context());
868 873
869 FOR_NB_SCREENS(i) 874 FOR_NB_SCREENS(i)
870 viewportmanager_theme_enable(i, false, NULL); 875 viewportmanager_theme_enable(i, false, NULL);
@@ -879,6 +884,7 @@ int plugin_load(const char* plugin, const void* parameter)
879 884
880 rc = p_hdr->entry_point(parameter); 885 rc = p_hdr->entry_point(parameter);
881 886
887 tree_unlock_cache(tree_get_context());
882 pop_current_activity(); 888 pop_current_activity();
883 889
884 if (!pfn_tsr_exit) 890 if (!pfn_tsr_exit)
diff --git a/apps/plugin.h b/apps/plugin.h
index d70e5634f9..1d8413f6df 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -926,6 +926,8 @@ struct plugin_api {
926 926
927 /* new stuff at the end, sort into place next time 927 /* new stuff at the end, sort into place next time
928 the API gets incompatible */ 928 the API gets incompatible */
929 struct entry* (*tree_get_entries)(struct tree_context* t);
930 struct entry* (*tree_get_entry_at)(struct tree_context* t, int index);
929}; 931};
930 932
931/* plugin header */ 933/* plugin header */
diff --git a/apps/plugins/imageviewer/imageviewer.c b/apps/plugins/imageviewer/imageviewer.c
index 80e1ba41bf..044c835d00 100644
--- a/apps/plugins/imageviewer/imageviewer.c
+++ b/apps/plugins/imageviewer/imageviewer.c
@@ -136,7 +136,7 @@ static enum image_type image_type = IMAGE_UNKNOWN;
136static void get_pic_list(void) 136static void get_pic_list(void)
137{ 137{
138 struct tree_context *tree = rb->tree_get_context(); 138 struct tree_context *tree = rb->tree_get_context();
139 struct entry *dircache = tree->cache.entries; 139 struct entry *dircache = rb->tree_get_entries(tree);
140 int i; 140 int i;
141 char *pname; 141 char *pname;
142 142
diff --git a/apps/plugins/mikmod/mikmod.c b/apps/plugins/mikmod/mikmod.c
index dff0fce685..d132f80498 100644
--- a/apps/plugins/mikmod/mikmod.c
+++ b/apps/plugins/mikmod/mikmod.c
@@ -185,7 +185,7 @@ bool mod_ext(const char ext[])
185void get_mod_list(void) 185void get_mod_list(void)
186{ 186{
187 struct tree_context *tree = rb->tree_get_context(); 187 struct tree_context *tree = rb->tree_get_context();
188 struct entry *dircache = tree->cache.entries; 188 struct entry *dircache = rb->tree_get_entries(tree);
189 int i; 189 int i;
190 char *pname; 190 char *pname;
191 191
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
index 156ec019c1..84eae42a75 100644
--- a/apps/plugins/mpegplayer/mpegplayer.c
+++ b/apps/plugins/mpegplayer/mpegplayer.c
@@ -1876,7 +1876,7 @@ static bool is_videofile(const char* file)
1876static bool get_videofile(int direction, char* videofile, size_t bufsize) 1876static bool get_videofile(int direction, char* videofile, size_t bufsize)
1877{ 1877{
1878 struct tree_context *tree = rb->tree_get_context(); 1878 struct tree_context *tree = rb->tree_get_context();
1879 struct entry *dircache = tree->cache.entries; 1879 struct entry *dircache = rb->tree_get_entries(tree);
1880 int i, step, end, found = 0; 1880 int i, step, end, found = 0;
1881 char *videoname = rb->strrchr(videofile, '/') + 1; 1881 char *videoname = rb->strrchr(videofile, '/') + 1;
1882 size_t rest = bufsize - (videoname - videofile) - 1; 1882 size_t rest = bufsize - (videoname - videofile) - 1;
diff --git a/apps/plugins/rockpaint.c b/apps/plugins/rockpaint.c
index add09c7fef..d1cc8f272a 100644
--- a/apps/plugins/rockpaint.c
+++ b/apps/plugins/rockpaint.c
@@ -948,7 +948,7 @@ static bool browse_fonts( char *dst, int dst_size )
948 948
949 tree = rb->tree_get_context(); 949 tree = rb->tree_get_context();
950 backup = *tree; 950 backup = *tree;
951 dc = tree->cache.entries; 951 dc = rb->tree_get_entries(tree);
952 a = backup.currdir+rb->strlen(backup.currdir)-1; 952 a = backup.currdir+rb->strlen(backup.currdir)-1;
953 if( *a != '/' ) 953 if( *a != '/' )
954 { 954 {
diff --git a/apps/scrobbler.c b/apps/scrobbler.c
index a6307d5dd7..78414f3d88 100644
--- a/apps/scrobbler.c
+++ b/apps/scrobbler.c
@@ -255,6 +255,11 @@ int scrobbler_init(void)
255 return -1; 255 return -1;
256 256
257 scrobbler_cache = core_alloc("scrobbler", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); 257 scrobbler_cache = core_alloc("scrobbler", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
258 if (scrobbler_cache <= 0)
259 {
260 logf("SCROOBLER: OOM");
261 return -1;
262 }
258 263
259 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event); 264 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event);
260 cache_pos = 0; 265 cache_pos = 0;
@@ -288,6 +293,9 @@ void scrobbler_shutdown(void)
288 { 293 {
289 remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event); 294 remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
290 scrobbler_initialised = false; 295 scrobbler_initialised = false;
296 /* get rid of the buffer */
297 core_free(scrobbler_cache);
298 scrobbler_cache = 0;
291 } 299 }
292} 300}
293 301
diff --git a/apps/tagcache.c b/apps/tagcache.c
index 753675f906..5ab77264f6 100644
--- a/apps/tagcache.c
+++ b/apps/tagcache.c
@@ -222,6 +222,8 @@ struct statefile_header {
222 222
223/* Pointer to allocated ramcache_header */ 223/* Pointer to allocated ramcache_header */
224static struct ramcache_header *ramcache_hdr; 224static struct ramcache_header *ramcache_hdr;
225/* lock entity to temporarily prevent ramcache_hdr from moving */
226static int move_lock;
225#endif 227#endif
226 228
227/** 229/**
@@ -1035,6 +1037,8 @@ static bool check_clauses(struct tagcache_search *tcs,
1035 { 1037 {
1036 tfe = (struct tagfile_entry *) 1038 tfe = (struct tagfile_entry *)
1037 &ramcache_hdr->tags[clause->tag][seek]; 1039 &ramcache_hdr->tags[clause->tag][seek];
1040 /* str points to movable data, but no locking required here,
1041 * as no yield() is following */
1038 str = tfe->tag_data; 1042 str = tfe->tag_data;
1039 } 1043 }
1040 } 1044 }
@@ -1149,9 +1153,11 @@ static bool build_lookup_list(struct tagcache_search *tcs)
1149# endif 1153# endif
1150 ) 1154 )
1151 { 1155 {
1156 move_lock++; /* lock because below makes a pointer to movable data */
1152 for (i = tcs->seek_pos; i < current_tcmh.tch.entry_count; i++) 1157 for (i = tcs->seek_pos; i < current_tcmh.tch.entry_count; i++)
1153 { 1158 {
1154 struct tagcache_seeklist_entry *seeklist; 1159 struct tagcache_seeklist_entry *seeklist;
1160 /* idx points to movable data, don't yield or reload */
1155 struct index_entry *idx = &ramcache_hdr->indices[i]; 1161 struct index_entry *idx = &ramcache_hdr->indices[i];
1156 if (tcs->seek_list_count == SEEK_LIST_SIZE) 1162 if (tcs->seek_list_count == SEEK_LIST_SIZE)
1157 break ; 1163 break ;
@@ -1175,8 +1181,7 @@ static bool build_lookup_list(struct tagcache_search *tcs)
1175 /* Check for conditions. */ 1181 /* Check for conditions. */
1176 if (!check_clauses(tcs, idx, tcs->clause, tcs->clause_count)) 1182 if (!check_clauses(tcs, idx, tcs->clause, tcs->clause_count))
1177 continue; 1183 continue;
1178 1184 /* Add to the seek list if not already in uniq buffer (doesn't yield)*/
1179 /* Add to the seek list if not already in uniq buffer. */
1180 if (!add_uniqbuf(tcs, idx->tag_seek[tcs->type])) 1185 if (!add_uniqbuf(tcs, idx->tag_seek[tcs->type]))
1181 continue; 1186 continue;
1182 1187
@@ -1187,6 +1192,7 @@ static bool build_lookup_list(struct tagcache_search *tcs)
1187 seeklist->idx_id = i; 1192 seeklist->idx_id = i;
1188 tcs->seek_list_count++; 1193 tcs->seek_list_count++;
1189 } 1194 }
1195 move_lock--;
1190 1196
1191 tcs->seek_pos = i; 1197 tcs->seek_pos = i;
1192 1198
@@ -1538,10 +1544,11 @@ static bool get_next(struct tagcache_search *tcs)
1538 struct tagfile_entry *ep; 1544 struct tagfile_entry *ep;
1539 1545
1540 ep = (struct tagfile_entry *)&ramcache_hdr->tags[tcs->type][tcs->position]; 1546 ep = (struct tagfile_entry *)&ramcache_hdr->tags[tcs->type][tcs->position];
1541 tcs->result = ep->tag_data; 1547 /* don't return ep->tag_data directly as it may move */
1542 tcs->result_len = strlen(tcs->result) + 1; 1548 tcs->result_len = strlcpy(buf, ep->tag_data, sizeof(buf)) + 1;
1549 tcs->result = buf;
1543 tcs->idx_id = ep->idx_id; 1550 tcs->idx_id = ep->idx_id;
1544 tcs->ramresult = true; 1551 tcs->ramresult = false; /* was true before we copied to buf too */
1545 1552
1546 /* Increase position for the next run. This may get overwritten. */ 1553 /* Increase position for the next run. This may get overwritten. */
1547 tcs->position += sizeof(struct tagfile_entry) + ep->tag_length; 1554 tcs->position += sizeof(struct tagfile_entry) + ep->tag_length;
@@ -1703,15 +1710,34 @@ bool tagcache_fill_tags(struct mp3entry *id3, const char *filename)
1703 entry = &ramcache_hdr->indices[idx_id]; 1710 entry = &ramcache_hdr->indices[idx_id];
1704 1711
1705 memset(id3, 0, sizeof(struct mp3entry)); 1712 memset(id3, 0, sizeof(struct mp3entry));
1706 1713 char* buf = id3->id3v2buf;
1707 id3->title = get_tag_string(entry, tag_title); 1714 ssize_t remaining = sizeof(id3->id3v2buf);
1708 id3->artist = get_tag_string(entry, tag_artist); 1715
1709 id3->album = get_tag_string(entry, tag_album); 1716 /* this macro sets id3 strings by copying to the id3v2buf */
1710 id3->genre_string = get_tag_string(entry, tag_genre); 1717#define SET(x, y) do \
1711 id3->composer = get_tag_string(entry, tag_composer); 1718 { \
1712 id3->comment = get_tag_string(entry, tag_comment); 1719 if (remaining > 0) \
1713 id3->albumartist = get_tag_string(entry, tag_albumartist); 1720 { \
1714 id3->grouping = get_tag_string(entry, tag_grouping); 1721 x = NULL; /* initialize with null if tag doesn't exist */ \
1722 char* src = get_tag_string(entry, y); \
1723 if (src) \
1724 { \
1725 x = buf; \
1726 size_t len = strlcpy(buf, src, remaining) +1; \
1727 buf += len; remaining -= len; \
1728 } \
1729 } \
1730 } while(0)
1731
1732
1733 SET(id3->title, tag_title);
1734 SET(id3->artist, tag_artist);
1735 SET(id3->album, tag_album);
1736 SET(id3->genre_string, tag_genre);
1737 SET(id3->composer, tag_composer);
1738 SET(id3->comment, tag_comment);
1739 SET(id3->albumartist, tag_albumartist);
1740 SET(id3->grouping, tag_grouping);
1715 1741
1716 id3->length = get_tag_numeric(entry, tag_length, idx_id); 1742 id3->length = get_tag_numeric(entry, tag_length, idx_id);
1717 id3->playcount = get_tag_numeric(entry, tag_playcount, idx_id); 1743 id3->playcount = get_tag_numeric(entry, tag_playcount, idx_id);
@@ -2903,6 +2929,9 @@ static bool commit(void)
2903#ifdef HAVE_DIRCACHE 2929#ifdef HAVE_DIRCACHE
2904 bool dircache_buffer_stolen = false; 2930 bool dircache_buffer_stolen = false;
2905#endif 2931#endif
2932#ifdef HAVE_TC_RAMCACHE
2933 bool ramcache_buffer_stolen = false;
2934#endif
2906 bool local_allocation = false; 2935 bool local_allocation = false;
2907 2936
2908 logf("committing tagcache"); 2937 logf("committing tagcache");
@@ -2976,6 +3005,8 @@ static bool commit(void)
2976 tempbuf = (char *)(ramcache_hdr + 1); 3005 tempbuf = (char *)(ramcache_hdr + 1);
2977 tempbuf_size = tc_stat.ramcache_allocated - sizeof(struct ramcache_header) - 128; 3006 tempbuf_size = tc_stat.ramcache_allocated - sizeof(struct ramcache_header) - 128;
2978 tempbuf_size &= ~0x03; 3007 tempbuf_size &= ~0x03;
3008 move_lock++;
3009 ramcache_buffer_stolen = true;
2979 } 3010 }
2980#endif 3011#endif
2981 3012
@@ -3072,6 +3103,8 @@ static bool commit(void)
3072#endif 3103#endif
3073 3104
3074#ifdef HAVE_TC_RAMCACHE 3105#ifdef HAVE_TC_RAMCACHE
3106 if (ramcache_buffer_stolen)
3107 move_lock--;
3075 /* Reload tagcache. */ 3108 /* Reload tagcache. */
3076 if (tc_stat.ramcache_allocated > 0) 3109 if (tc_stat.ramcache_allocated > 0)
3077 tagcache_start_scan(); 3110 tagcache_start_scan();
@@ -3689,9 +3722,11 @@ static bool delete_entry(long idx_id)
3689 { 3722 {
3690 struct tagfile_entry *tfe; 3723 struct tagfile_entry *tfe;
3691 int32_t *seek = &ramcache_hdr->indices[idx_id].tag_seek[tag]; 3724 int32_t *seek = &ramcache_hdr->indices[idx_id].tag_seek[tag];
3692 3725
3693 tfe = (struct tagfile_entry *)&ramcache_hdr->tags[tag][*seek]; 3726 tfe = (struct tagfile_entry *)&ramcache_hdr->tags[tag][*seek];
3727 move_lock++; /* protect tfe and seek if crc_32() yield()s */
3694 *seek = crc_32(tfe->tag_data, strlen(tfe->tag_data), 0xffffffff); 3728 *seek = crc_32(tfe->tag_data, strlen(tfe->tag_data), 0xffffffff);
3729 move_lock--;
3695 myidx.tag_seek[tag] = *seek; 3730 myidx.tag_seek[tag] = *seek;
3696 } 3731 }
3697 else 3732 else
@@ -3813,6 +3848,30 @@ static bool check_event_queue(void)
3813#endif 3848#endif
3814 3849
3815#ifdef HAVE_TC_RAMCACHE 3850#ifdef HAVE_TC_RAMCACHE
3851
3852static void fix_ramcache(void* old_addr, void* new_addr)
3853{
3854 ptrdiff_t offpos = new_addr - old_addr;
3855 for (int i = 0; i < TAG_COUNT; i++)
3856 ramcache_hdr->tags[i] += offpos;
3857}
3858
3859static int move_cb(int handle, void* current, void* new)
3860{
3861 (void)handle;
3862 if (move_lock > 0)
3863 return BUFLIB_CB_CANNOT_MOVE;
3864
3865 fix_ramcache(current, new);
3866 ramcache_hdr = new;
3867 return BUFLIB_CB_OK;
3868}
3869
3870static struct buflib_callbacks ops = {
3871 .move_callback = move_cb,
3872 .shrink_callback = NULL,
3873};
3874
3816static bool allocate_tagcache(void) 3875static bool allocate_tagcache(void)
3817{ 3876{
3818 struct master_header tcmh; 3877 struct master_header tcmh;
@@ -3833,7 +3892,7 @@ static bool allocate_tagcache(void)
3833 */ 3892 */
3834 tc_stat.ramcache_allocated = tcmh.tch.datasize + 256 + TAGCACHE_RESERVE + 3893 tc_stat.ramcache_allocated = tcmh.tch.datasize + 256 + TAGCACHE_RESERVE +
3835 sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *); 3894 sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *);
3836 int handle = core_alloc("tc ramcache", tc_stat.ramcache_allocated); 3895 int handle = core_alloc_ex("tc ramcache", tc_stat.ramcache_allocated, &ops);
3837 ramcache_hdr = core_get_data(handle); 3896 ramcache_hdr = core_get_data(handle);
3838 memset(ramcache_hdr, 0, sizeof(struct ramcache_header)); 3897 memset(ramcache_hdr, 0, sizeof(struct ramcache_header));
3839 memcpy(&current_tcmh, &tcmh, sizeof current_tcmh); 3898 memcpy(&current_tcmh, &tcmh, sizeof current_tcmh);
@@ -3871,12 +3930,13 @@ static bool tagcache_dumpload(void)
3871 3930
3872 3931
3873 /* Lets allocate real memory and load it */ 3932 /* Lets allocate real memory and load it */
3874 handle = core_alloc("tc ramcache", shdr.tc_stat.ramcache_allocated); 3933 handle = core_alloc_ex("tc ramcache", shdr.tc_stat.ramcache_allocated, &ops);
3875 ramcache_hdr = core_get_data(handle); 3934 ramcache_hdr = core_get_data(handle);
3935 moev_lock++;
3876 rc = read(fd, ramcache_hdr, shdr.tc_stat.ramcache_allocated); 3936 rc = read(fd, ramcache_hdr, shdr.tc_stat.ramcache_allocated);
3937 move_lock--;
3877 close(fd); 3938 close(fd);
3878 3939
3879 offpos = (long)ramcache_hdr - (long)shdr.hdr;
3880 if (rc != shdr.tc_stat.ramcache_allocated) 3940 if (rc != shdr.tc_stat.ramcache_allocated)
3881 { 3941 {
3882 logf("read failure!"); 3942 logf("read failure!");
@@ -3887,8 +3947,7 @@ static bool tagcache_dumpload(void)
3887 memcpy(&tc_stat, &shdr.tc_stat, sizeof(struct tagcache_stat)); 3947 memcpy(&tc_stat, &shdr.tc_stat, sizeof(struct tagcache_stat));
3888 3948
3889 /* Now fix the pointers */ 3949 /* Now fix the pointers */
3890 for (i = 0; i < TAG_COUNT; i++) 3950 fix_ramcache(shdr.hdr, ramcache_hdr);
3891 ramcache_hdr->tags[i] += offpos;
3892 3951
3893 /* Load the tagcache master header (should match the actual DB file header). */ 3952 /* Load the tagcache master header (should match the actual DB file header). */
3894 memcpy(&current_tcmh, &shdr.mh, sizeof current_tcmh); 3953 memcpy(&current_tcmh, &shdr.mh, sizeof current_tcmh);
@@ -3919,7 +3978,9 @@ static bool tagcache_dumpsave(void)
3919 write(fd, &shdr, sizeof shdr); 3978 write(fd, &shdr, sizeof shdr);
3920 3979
3921 /* And dump the data too */ 3980 /* And dump the data too */
3981 move_lock++;
3922 write(fd, ramcache_hdr, tc_stat.ramcache_allocated); 3982 write(fd, ramcache_hdr, tc_stat.ramcache_allocated);
3983 move_lock--;
3923 close(fd); 3984 close(fd);
3924 3985
3925 return true; 3986 return true;
@@ -3962,7 +4023,8 @@ static bool load_tagcache(void)
3962 4023
3963 /* Master header copy should already match, this can be redundant to do. */ 4024 /* Master header copy should already match, this can be redundant to do. */
3964 memcpy(&current_tcmh, &tcmh, sizeof current_tcmh); 4025 memcpy(&current_tcmh, &tcmh, sizeof current_tcmh);
3965 4026
4027 move_lock++; /* lock for the reset of the scan, simpler to handle */
3966 idx = ramcache_hdr->indices; 4028 idx = ramcache_hdr->indices;
3967 4029
3968 /* Load the master index table. */ 4030 /* Load the master index table. */
@@ -3972,8 +4034,7 @@ static bool load_tagcache(void)
3972 if (bytesleft < 0) 4034 if (bytesleft < 0)
3973 { 4035 {
3974 logf("too big tagcache."); 4036 logf("too big tagcache.");
3975 close(fd); 4037 goto failure;
3976 return false;
3977 } 4038 }
3978 4039
3979 /* DEBUG: After tagcache commit and dircache rebuild, hdr-sturcture 4040 /* DEBUG: After tagcache commit and dircache rebuild, hdr-sturcture
@@ -3982,8 +4043,7 @@ static bool load_tagcache(void)
3982 if (rc != sizeof(struct index_entry)) 4043 if (rc != sizeof(struct index_entry))
3983 { 4044 {
3984 logf("read error #10"); 4045 logf("read error #10");
3985 close(fd); 4046 goto failure;
3986 return false;
3987 } 4047 }
3988 4048
3989 idx++; 4049 idx++;
@@ -4010,7 +4070,7 @@ static bool load_tagcache(void)
4010 p += sizeof(struct tagcache_header); 4070 p += sizeof(struct tagcache_header);
4011 4071
4012 if ( (fd = open_tag_fd(tch, tag, false)) < 0) 4072 if ( (fd = open_tag_fd(tch, tag, false)) < 0)
4013 return false; 4073 goto failure_nofd;
4014 4074
4015 for (ramcache_hdr->entry_count[tag] = 0; 4075 for (ramcache_hdr->entry_count[tag] = 0;
4016 ramcache_hdr->entry_count[tag] < tch->entry_count; 4076 ramcache_hdr->entry_count[tag] < tch->entry_count;
@@ -4022,7 +4082,7 @@ static bool load_tagcache(void)
4022 { 4082 {
4023 /* Abort if we got a critical event in queue */ 4083 /* Abort if we got a critical event in queue */
4024 if (check_event_queue()) 4084 if (check_event_queue())
4025 return false; 4085 goto failure;
4026 } 4086 }
4027 4087
4028 fe = (struct tagfile_entry *)p; 4088 fe = (struct tagfile_entry *)p;
@@ -4032,8 +4092,7 @@ static bool load_tagcache(void)
4032 { 4092 {
4033 /* End of lookup table. */ 4093 /* End of lookup table. */
4034 logf("read error #11"); 4094 logf("read error #11");
4035 close(fd); 4095 goto failure;
4036 return false;
4037 } 4096 }
4038 4097
4039 /* We have a special handling for the filename tags. */ 4098 /* We have a special handling for the filename tags. */
@@ -4051,16 +4110,14 @@ static bool load_tagcache(void)
4051 buf[10] = '\0'; 4110 buf[10] = '\0';
4052 logf("TAG:%s", buf); 4111 logf("TAG:%s", buf);
4053 logf("too long filename"); 4112 logf("too long filename");
4054 close(fd); 4113 goto failure;
4055 return false;
4056 } 4114 }
4057 4115
4058 rc = read(fd, buf, fe->tag_length); 4116 rc = read(fd, buf, fe->tag_length);
4059 if (rc != fe->tag_length) 4117 if (rc != fe->tag_length)
4060 { 4118 {
4061 logf("read error #12"); 4119 logf("read error #12");
4062 close(fd); 4120 goto failure;
4063 return false;
4064 } 4121 }
4065 4122
4066 /* Check if the entry has already been removed */ 4123 /* Check if the entry has already been removed */
@@ -4071,15 +4128,13 @@ static bool load_tagcache(void)
4071 if (idx->flag & FLAG_DIRCACHE) 4128 if (idx->flag & FLAG_DIRCACHE)
4072 { 4129 {
4073 logf("internal error!"); 4130 logf("internal error!");
4074 close(fd); 4131 goto failure;
4075 return false;
4076 } 4132 }
4077 4133
4078 if (idx->tag_seek[tag] != pos) 4134 if (idx->tag_seek[tag] != pos)
4079 { 4135 {
4080 logf("corrupt data structures!"); 4136 logf("corrupt data structures!");
4081 close(fd); 4137 goto failure;
4082 return false;
4083 } 4138 }
4084 4139
4085# ifdef HAVE_DIRCACHE 4140# ifdef HAVE_DIRCACHE
@@ -4126,8 +4181,7 @@ static bool load_tagcache(void)
4126 logf("too big tagcache #2"); 4181 logf("too big tagcache #2");
4127 logf("tl: %ld", fe->tag_length); 4182 logf("tl: %ld", fe->tag_length);
4128 logf("bl: %ld", bytesleft); 4183 logf("bl: %ld", bytesleft);
4129 close(fd); 4184 goto failure;
4130 return false;
4131 } 4185 }
4132 4186
4133 p = fe->tag_data; 4187 p = fe->tag_data;
@@ -4141,8 +4195,7 @@ static bool load_tagcache(void)
4141 logf("len=0x%04lx", fe->tag_length); // 0x4000 4195 logf("len=0x%04lx", fe->tag_length); // 0x4000
4142 logf("pos=0x%04lx", lseek(fd, 0, SEEK_CUR)); // 0x433 4196 logf("pos=0x%04lx", lseek(fd, 0, SEEK_CUR)); // 0x433
4143 logf("tag=0x%02x", tag); // 0x00 4197 logf("tag=0x%02x", tag); // 0x00
4144 close(fd); 4198 goto failure;
4145 return false;
4146 } 4199 }
4147 } 4200 }
4148 close(fd); 4201 close(fd);
@@ -4151,7 +4204,14 @@ static bool load_tagcache(void)
4151 tc_stat.ramcache_used = tc_stat.ramcache_allocated - bytesleft; 4204 tc_stat.ramcache_used = tc_stat.ramcache_allocated - bytesleft;
4152 logf("tagcache loaded into ram!"); 4205 logf("tagcache loaded into ram!");
4153 4206
4207 move_lock--;
4154 return true; 4208 return true;
4209
4210failure:
4211 close(fd);
4212failure_nofd:
4213 move_lock--;
4214 return false;
4155} 4215}
4156#endif /* HAVE_TC_RAMCACHE */ 4216#endif /* HAVE_TC_RAMCACHE */
4157 4217
diff --git a/apps/tagcache.h b/apps/tagcache.h
index 393a2905f5..6c13efdd0e 100644
--- a/apps/tagcache.h
+++ b/apps/tagcache.h
@@ -190,7 +190,9 @@ struct tagcache_search {
190 190
191 /* Exported variables. */ 191 /* Exported variables. */
192 bool ramsearch; /* Is ram copy of the tagcache being used. */ 192 bool ramsearch; /* Is ram copy of the tagcache being used. */
193 bool ramresult; /* False if result is not static, and must be copied. */ 193 bool ramresult; /* False if result is not static, and must be copied.
194 Currently always false since ramresult buffer is
195 movable */
194 int type; /* The tag type to be searched. Only nonvirtual tags */ 196 int type; /* The tag type to be searched. Only nonvirtual tags */
195 char *result; /* The result data for all tags. */ 197 char *result; /* The result data for all tags. */
196 int result_len; /* Length of the result including \0 */ 198 int result_len; /* Length of the result including \0 */
diff --git a/apps/tagtree.c b/apps/tagtree.c
index 0d4330bac8..5766d2892e 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -53,6 +53,7 @@
53#include "storage.h" 53#include "storage.h"
54#include "dir.h" 54#include "dir.h"
55#include "playback.h" 55#include "playback.h"
56#include "panic.h"
56 57
57#define str_or_empty(x) (x ? x : "(NULL)") 58#define str_or_empty(x) (x ? x : "(NULL)")
58 59
@@ -60,6 +61,17 @@
60 61
61static int tagtree_play_folder(struct tree_context* c); 62static int tagtree_play_folder(struct tree_context* c);
62 63
64/* this needs to be same size as struct entry (tree.h) and name needs to be
65 * the first; so that they're compatible enough to walk arrays of both
66 * derefencing the name member*/
67struct tagentry {
68 char* name;
69 int newtable;
70 int extraseek;
71};
72
73static struct tagentry* tagtree_get_entry(struct tree_context *c, int id);
74
63#define SEARCHSTR_SIZE 256 75#define SEARCHSTR_SIZE 256
64 76
65enum table { 77enum table {
@@ -96,7 +108,7 @@ enum variables {
96 108
97/* Capacity 10 000 entries (for example 10k different artists) */ 109/* Capacity 10 000 entries (for example 10k different artists) */
98#define UNIQBUF_SIZE (64*1024) 110#define UNIQBUF_SIZE (64*1024)
99static long *uniqbuf; 111static long uniqbuf[UNIQBUF_SIZE / sizeof(long)];
100 112
101#define MAX_TAGS 5 113#define MAX_TAGS 5
102#define MAX_MENU_ID_SIZE 32 114#define MAX_MENU_ID_SIZE 32
@@ -163,8 +175,8 @@ struct match
163/* Statusbar text of the current view. */ 175/* Statusbar text of the current view. */
164static char current_title[MAX_TAGS][128]; 176static char current_title[MAX_TAGS][128];
165 177
166static struct menu_root *menus[TAGMENU_MAX_MENUS]; 178static struct menu_root * menus[TAGMENU_MAX_MENUS];
167static struct menu_root *menu; 179static struct menu_root * menu;
168static struct search_instruction *csi; 180static struct search_instruction *csi;
169static const char *strp; 181static const char *strp;
170static int menu_count; 182static int menu_count;
@@ -176,8 +188,74 @@ static int current_entry_count;
176static struct tree_context *tc; 188static struct tree_context *tc;
177 189
178/* a few memory alloc helper */ 190/* a few memory alloc helper */
179static int tagtree_handle; 191static int tagtree_handle, lock_count;
180static size_t tagtree_bufsize, tagtree_buf_used; 192static size_t tagtree_bufsize, tagtree_buf_used;
193
194#define UPDATE(x, y) { x = (typeof(x))((char*)(x) + (y)); }
195static int move_callback(int handle, void* current, void* new)
196{
197 (void)handle; (void)current; (void)new;
198 ptrdiff_t diff = new - current;
199
200 if (lock_count > 0)
201 return BUFLIB_CB_CANNOT_MOVE;
202
203 UPDATE(menu, diff);
204 /* loop over menus */
205 for(int i = 0; i < menu_count; i++)
206 {
207 struct menu_root* menu = menus[i];
208 /* then over the menu_entries of a menu */
209 for(int j = 0; j < menu->itemcount; j++)
210 {
211 struct menu_entry* mentry = menu->items[j];
212 /* then over the search_instructions of each menu_entry */
213 for(int k = 0; k < mentry->si.tagorder_count; k++)
214 {
215 for(int l = 0; l < mentry->si.clause_count[k]; l++)
216 {
217 UPDATE(mentry->si.clause[k][l]->str, diff);
218 UPDATE(mentry->si.clause[k][l], diff);
219 }
220 }
221 UPDATE(menu->items[j], diff);
222 }
223 UPDATE(menus[i], diff);
224 }
225
226 /* now the same game for formats */
227 for(int i = 0; i < format_count; i++)
228 {
229 for(int j = 0; j < formats[i]->clause_count; j++)
230 {
231 UPDATE(formats[i]->clause[j]->str, diff);
232 UPDATE(formats[i]->clause[j], diff);
233 }
234
235 if (formats[i]->formatstr)
236 UPDATE(formats[i]->formatstr, diff);
237
238 UPDATE(formats[i], diff);
239 }
240 return BUFLIB_CB_OK;
241}
242#undef UPDATE
243
244static inline void tagtree_lock(void)
245{
246 lock_count++;
247}
248
249static inline void tagtree_unlock(void)
250{
251 lock_count--;
252}
253
254static struct buflib_callbacks ops = {
255 .move_callback = move_callback,
256 .shrink_callback = NULL,
257};
258
181static void* tagtree_alloc(size_t size) 259static void* tagtree_alloc(size_t size)
182{ 260{
183 char* buf = core_get_data(tagtree_handle) + tagtree_buf_used; 261 char* buf = core_get_data(tagtree_handle) + tagtree_buf_used;
@@ -201,6 +279,7 @@ static char* tagtree_strdup(const char* buf)
201 return dest; 279 return dest;
202} 280}
203 281
282/* save to call without locking */
204static int get_token_str(char *buf, int size) 283static int get_token_str(char *buf, int size)
205{ 284{
206 /* Find the start. */ 285 /* Find the start. */
@@ -510,7 +589,8 @@ static int add_format(const char *buf)
510 { 589 {
511 int clause_count = 0; 590 int clause_count = 0;
512 strp++; 591 strp++;
513 592
593 tagtree_lock();
514 while (1) 594 while (1)
515 { 595 {
516 struct tagcache_search_clause *newclause; 596 struct tagcache_search_clause *newclause;
@@ -529,6 +609,7 @@ static int add_format(const char *buf)
529 609
530 clause_count++; 610 clause_count++;
531 } 611 }
612 tagtree_unlock();
532 613
533 formats[format_count]->clause_count = clause_count; 614 formats[format_count]->clause_count = clause_count;
534 } 615 }
@@ -593,9 +674,14 @@ static int get_condition(struct search_instruction *inst)
593 strp++; 674 strp++;
594 new_clause->type = clause_logical_or; 675 new_clause->type = clause_logical_or;
595 } 676 }
596 else if (!read_clause(new_clause)) 677 else
597 return -1; 678 {
598 679 tagtree_lock();
680 bool ret = read_clause(new_clause);
681 tagtree_unlock();
682 if (!ret)
683 return -1;
684 }
599 inst->clause_count[inst->tagorder_count]++; 685 inst->clause_count[inst->tagorder_count]++;
600 686
601 return 1; 687 return 1;
@@ -616,7 +702,6 @@ static bool parse_search(struct menu_entry *entry, const char *str)
616 struct search_instruction *inst = &entry->si; 702 struct search_instruction *inst = &entry->si;
617 char buf[MAX_PATH]; 703 char buf[MAX_PATH];
618 int i; 704 int i;
619 struct menu_root *new_menu;
620 705
621 strp = str; 706 strp = str;
622 707
@@ -654,8 +739,7 @@ static bool parse_search(struct menu_entry *entry, const char *str)
654 739
655 /* Allocate a new menu unless link is found. */ 740 /* Allocate a new menu unless link is found. */
656 menus[menu_count] = tagtree_alloc0(sizeof(struct menu_root)); 741 menus[menu_count] = tagtree_alloc0(sizeof(struct menu_root));
657 new_menu = menus[menu_count]; 742 strlcpy(menus[menu_count]->id, buf, MAX_MENU_ID_SIZE);
658 strlcpy(new_menu->id, buf, MAX_MENU_ID_SIZE);
659 entry->link = menu_count; 743 entry->link = menu_count;
660 ++menu_count; 744 ++menu_count;
661 745
@@ -679,8 +763,11 @@ static bool parse_search(struct menu_entry *entry, const char *str)
679 break ; 763 break ;
680 764
681 logf("tag: %d", inst->tagorder[inst->tagorder_count]); 765 logf("tag: %d", inst->tagorder[inst->tagorder_count]);
682 766
767 tagtree_lock();
683 while ( (ret = get_condition(inst)) > 0 ) ; 768 while ( (ret = get_condition(inst)) > 0 ) ;
769 tagtree_unlock();
770
684 if (ret < 0) 771 if (ret < 0)
685 return false; 772 return false;
686 773
@@ -697,7 +784,7 @@ static int compare(const void *p1, const void *p2)
697{ 784{
698 struct tagentry *e1 = (struct tagentry *)p1; 785 struct tagentry *e1 = (struct tagentry *)p1;
699 struct tagentry *e2 = (struct tagentry *)p2; 786 struct tagentry *e2 = (struct tagentry *)p2;
700 787
701 if (sort_inverse) 788 if (sort_inverse)
702 return strncasecmp(e2->name, e1->name, MAX_PATH); 789 return strncasecmp(e2->name, e1->name, MAX_PATH);
703 790
@@ -1001,11 +1088,11 @@ static int parse_line(int n, char *buf, void *parameters)
1001 if (menu->items[menu->itemcount] == NULL) 1088 if (menu->items[menu->itemcount] == NULL)
1002 menu->items[menu->itemcount] = tagtree_alloc0(sizeof(struct menu_entry)); 1089 menu->items[menu->itemcount] = tagtree_alloc0(sizeof(struct menu_entry));
1003 1090
1004 if (!parse_search(menu->items[menu->itemcount], buf)) 1091 tagtree_lock();
1005 return 0; 1092 if (parse_search(menu->items[menu->itemcount], buf))
1006 1093 menu->itemcount++;
1007 menu->itemcount++; 1094 tagtree_unlock();
1008 1095
1009 return 0; 1096 return 0;
1010} 1097}
1011 1098
@@ -1040,15 +1127,20 @@ void tagtree_init(void)
1040 menu_count = 0; 1127 menu_count = 0;
1041 menu = NULL; 1128 menu = NULL;
1042 rootmenu = -1; 1129 rootmenu = -1;
1043 tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, NULL); 1130 tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, &ops);
1044 parse_menu(FILE_SEARCH_INSTRUCTIONS); 1131 parse_menu(FILE_SEARCH_INSTRUCTIONS);
1132
1133 /* safety check since tree.c needs to cast tagentry to entry */
1134 if (sizeof(struct tagentry) != sizeof(struct entry))
1135 panicf("tagentry(%zu) and entry mismatch(%zu)",
1136 sizeof(struct tagentry), sizeof(struct entry));
1137 if (lock_count > 0)
1138 panicf("tagtree locked after parsing");
1045 1139
1046 /* If no root menu is set, assume it's the first single menu 1140 /* If no root menu is set, assume it's the first single menu
1047 * we have. That shouldn't normally happen. */ 1141 * we have. That shouldn't normally happen. */
1048 if (rootmenu < 0) 1142 if (rootmenu < 0)
1049 rootmenu = 0; 1143 rootmenu = 0;
1050
1051 uniqbuf = tagtree_alloc(UNIQBUF_SIZE);
1052 1144
1053 add_event(PLAYBACK_EVENT_TRACK_BUFFER, false, tagtree_buffer_event); 1145 add_event(PLAYBACK_EVENT_TRACK_BUFFER, false, tagtree_buffer_event);
1054 add_event(PLAYBACK_EVENT_TRACK_FINISH, false, tagtree_track_finish_event); 1146 add_event(PLAYBACK_EVENT_TRACK_FINISH, false, tagtree_track_finish_event);
@@ -1181,10 +1273,14 @@ static int format_str(struct tagcache_search *tcs, struct display_format *fmt,
1181 return 0; 1273 return 0;
1182} 1274}
1183 1275
1276static struct tagentry* get_entries(struct tree_context *tc)
1277{
1278 return core_get_data(tc->cache.entries_handle);
1279}
1280
1184static int retrieve_entries(struct tree_context *c, int offset, bool init) 1281static int retrieve_entries(struct tree_context *c, int offset, bool init)
1185{ 1282{
1186 struct tagcache_search tcs; 1283 struct tagcache_search tcs;
1187 struct tagentry *dptr = c->cache.entries;
1188 struct display_format *fmt; 1284 struct display_format *fmt;
1189 int i; 1285 int i;
1190 int namebufused = 0; 1286 int namebufused = 0;
@@ -1242,7 +1338,10 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1242 csi->result_seek[i]); 1338 csi->result_seek[i]);
1243 } 1339 }
1244 } 1340 }
1245 1341
1342 /* because tagcache saves the clauses, we need to lock the buffer
1343 * for the entire duration of the search */
1344 tagtree_lock();
1246 for (i = 0; i <= level; i++) 1345 for (i = 0; i <= level; i++)
1247 { 1346 {
1248 int j; 1347 int j;
@@ -1276,6 +1375,10 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1276 strip = 0; 1375 strip = 0;
1277 } 1376 }
1278 1377
1378 /* lock buflib out due to possible yields */
1379 tree_lock_cache(c);
1380 struct tagentry *dptr = core_get_data(c->cache.entries_handle);
1381
1279 if (tag != tag_title && tag != tag_filename) 1382 if (tag != tag_title && tag != tag_filename)
1280 { 1383 {
1281 if (offset == 0) 1384 if (offset == 0)
@@ -1315,6 +1418,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1315 1418
1316 fmt = NULL; 1419 fmt = NULL;
1317 /* Check the format */ 1420 /* Check the format */
1421 tagtree_lock();
1318 for (i = 0; i < format_count; i++) 1422 for (i = 0; i < format_count; i++)
1319 { 1423 {
1320 if (formats[i]->group_id != csi->format_id[level]) 1424 if (formats[i]->group_id != csi->format_id[level])
@@ -1327,6 +1431,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1327 break; 1431 break;
1328 } 1432 }
1329 } 1433 }
1434 tagtree_unlock();
1330 1435
1331 if (strcmp(tcs.result, UNTAGGED) == 0) 1436 if (strcmp(tcs.result, UNTAGGED) == 0)
1332 { 1437 {
@@ -1337,7 +1442,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1337 1442
1338 if (!tcs.ramresult || fmt) 1443 if (!tcs.ramresult || fmt)
1339 { 1444 {
1340 dptr->name = &c->cache.name_buffer[namebufused]; 1445 dptr->name = core_get_data(c->cache.name_buffer_handle)+namebufused;
1341 1446
1342 if (fmt) 1447 if (fmt)
1343 { 1448 {
@@ -1354,6 +1459,8 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1354 { 1459 {
1355 logf("format_str() failed"); 1460 logf("format_str() failed");
1356 tagcache_search_finish(&tcs); 1461 tagcache_search_finish(&tcs);
1462 tree_unlock_cache(c);
1463 tagtree_unlock();
1357 return 0; 1464 return 0;
1358 } 1465 }
1359 else 1466 else
@@ -1392,6 +1499,8 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1392 if (!show_search_progress(false, total_count)) 1499 if (!show_search_progress(false, total_count))
1393 { /* user aborted */ 1500 { /* user aborted */
1394 tagcache_search_finish(&tcs); 1501 tagcache_search_finish(&tcs);
1502 tree_unlock_cache(c);
1503 tagtree_unlock();
1395 return current_entry_count; 1504 return current_entry_count;
1396 } 1505 }
1397 } 1506 }
@@ -1399,15 +1508,17 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1399 1508
1400 if (sort) 1509 if (sort)
1401 { 1510 {
1402 int entry_size = sizeof(struct tagentry); 1511 struct tagentry *entries = get_entries(c);
1403 qsort(c->cache.entries + special_entry_count * entry_size, 1512 qsort(&entries[special_entry_count],
1404 current_entry_count - special_entry_count, 1513 current_entry_count - special_entry_count,
1405 entry_size, compare); 1514 sizeof(struct tagentry), compare);
1406 } 1515 }
1407 1516
1408 if (!init) 1517 if (!init)
1409 { 1518 {
1410 tagcache_search_finish(&tcs); 1519 tagcache_search_finish(&tcs);
1520 tree_unlock_cache(c);
1521 tagtree_unlock();
1411 return current_entry_count; 1522 return current_entry_count;
1412 } 1523 }
1413 1524
@@ -1422,7 +1533,9 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1422 } 1533 }
1423 1534
1424 tagcache_search_finish(&tcs); 1535 tagcache_search_finish(&tcs);
1425 1536 tree_unlock_cache(c);
1537 tagtree_unlock();
1538
1426 if (!sort && (sort_inverse || sort_limit)) 1539 if (!sort && (sort_inverse || sort_limit))
1427 { 1540 {
1428 splashf(HZ*4, ID2P(LANG_SHOWDIR_BUFFER_FULL), total_count); 1541 splashf(HZ*4, ID2P(LANG_SHOWDIR_BUFFER_FULL), total_count);
@@ -1435,7 +1548,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1435 1548
1436 if (strip) 1549 if (strip)
1437 { 1550 {
1438 dptr = c->cache.entries; 1551 dptr = get_entries(c);
1439 for (i = special_entry_count; i < current_entry_count; i++, dptr++) 1552 for (i = special_entry_count; i < current_entry_count; i++, dptr++)
1440 { 1553 {
1441 int len = strlen(dptr->name); 1554 int len = strlen(dptr->name);
@@ -1446,14 +1559,14 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1446 dptr->name = &dptr->name[strip]; 1559 dptr->name = &dptr->name[strip];
1447 } 1560 }
1448 } 1561 }
1449 1562
1450 return total_count; 1563 return total_count;
1451 1564
1452} 1565}
1453 1566
1454static int load_root(struct tree_context *c) 1567static int load_root(struct tree_context *c)
1455{ 1568{
1456 struct tagentry *dptr = c->cache.entries; 1569 struct tagentry *dptr = core_get_data(c->cache.entries_handle);
1457 int i; 1570 int i;
1458 1571
1459 tc = c; 1572 tc = c;
@@ -1569,6 +1682,10 @@ int tagtree_enter(struct tree_context* c)
1569 c->pos_history[c->dirlevel] = c->firstpos; 1682 c->pos_history[c->dirlevel] = c->firstpos;
1570 c->dirlevel++; 1683 c->dirlevel++;
1571 1684
1685 /* lock buflib for possible I/O to protect dptr */
1686 tree_lock_cache(c);
1687 tagtree_lock();
1688
1572 switch (c->currtable) { 1689 switch (c->currtable) {
1573 case ROOT: 1690 case ROOT:
1574 c->currextra = newextra; 1691 c->currextra = newextra;
@@ -1634,6 +1751,8 @@ int tagtree_enter(struct tree_context* c)
1634 if (rc < 0 || !searchstring[0]) 1751 if (rc < 0 || !searchstring[0])
1635 { 1752 {
1636 tagtree_exit(c); 1753 tagtree_exit(c);
1754 tree_unlock_cache(c);
1755 tagtree_unlock();
1637 return 0; 1756 return 0;
1638 } 1757 }
1639 if (csi->clause[i][j]->numeric) 1758 if (csi->clause[i][j]->numeric)
@@ -1682,9 +1801,12 @@ int tagtree_enter(struct tree_context* c)
1682 c->dirlevel--; 1801 c->dirlevel--;
1683 break; 1802 break;
1684 } 1803 }
1804
1685 1805
1686 c->selected_item=0; 1806 c->selected_item=0;
1687 gui_synclist_select_item(&tree_lists, c->selected_item); 1807 gui_synclist_select_item(&tree_lists, c->selected_item);
1808 tree_unlock_cache(c);
1809 tagtree_unlock();
1688 1810
1689 return rc; 1811 return rc;
1690} 1812}
@@ -1704,14 +1826,13 @@ void tagtree_exit(struct tree_context* c)
1704int tagtree_get_filename(struct tree_context* c, char *buf, int buflen) 1826int tagtree_get_filename(struct tree_context* c, char *buf, int buflen)
1705{ 1827{
1706 struct tagcache_search tcs; 1828 struct tagcache_search tcs;
1707 struct tagentry *entry; 1829 int extraseek = tagtree_get_entry(c, c->selected_item)->extraseek;
1708 1830
1709 entry = tagtree_get_entry(c, c->selected_item);
1710 1831
1711 if (!tagcache_search(&tcs, tag_filename)) 1832 if (!tagcache_search(&tcs, tag_filename))
1712 return -1; 1833 return -1;
1713 1834
1714 if (!tagcache_retrieve(&tcs, entry->extraseek, tcs.type, buf, buflen)) 1835 if (!tagcache_retrieve(&tcs, extraseek, tcs.type, buf, buflen))
1715 { 1836 {
1716 tagcache_search_finish(&tcs); 1837 tagcache_search_finish(&tcs);
1717 return -2; 1838 return -2;
@@ -1790,9 +1911,9 @@ static bool insert_all_playlist(struct tree_context *c, int position, bool queue
1790 1911
1791bool tagtree_insert_selection_playlist(int position, bool queue) 1912bool tagtree_insert_selection_playlist(int position, bool queue)
1792{ 1913{
1793 struct tagentry *dptr;
1794 char buf[MAX_PATH]; 1914 char buf[MAX_PATH];
1795 int dirlevel = tc->dirlevel; 1915 int dirlevel = tc->dirlevel;
1916 int newtable;
1796 1917
1797 show_search_progress( 1918 show_search_progress(
1798#ifdef HAVE_DISK_STORAGE 1919#ifdef HAVE_DISK_STORAGE
@@ -1804,10 +1925,10 @@ bool tagtree_insert_selection_playlist(int position, bool queue)
1804 1925
1805 1926
1806 /* We need to set the table to allsubentries. */ 1927 /* We need to set the table to allsubentries. */
1807 dptr = tagtree_get_entry(tc, tc->selected_item); 1928 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
1808 1929
1809 /* Insert a single track? */ 1930 /* Insert a single track? */
1810 if (dptr->newtable == PLAYTRACK) 1931 if (newtable == PLAYTRACK)
1811 { 1932 {
1812 if (tagtree_get_filename(tc, buf, sizeof buf) < 0) 1933 if (tagtree_get_filename(tc, buf, sizeof buf) < 0)
1813 { 1934 {
@@ -1819,29 +1940,29 @@ bool tagtree_insert_selection_playlist(int position, bool queue)
1819 return true; 1940 return true;
1820 } 1941 }
1821 1942
1822 if (dptr->newtable == NAVIBROWSE) 1943 if (newtable == NAVIBROWSE)
1823 { 1944 {
1824 tagtree_enter(tc); 1945 tagtree_enter(tc);
1825 tagtree_load(tc); 1946 tagtree_load(tc);
1826 dptr = tagtree_get_entry(tc, tc->selected_item); 1947 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
1827 } 1948 }
1828 else if (dptr->newtable != ALLSUBENTRIES) 1949 else if (newtable != ALLSUBENTRIES)
1829 { 1950 {
1830 logf("unsupported table: %d", dptr->newtable); 1951 logf("unsupported table: %d", newtable);
1831 return false; 1952 return false;
1832 } 1953 }
1833 1954
1834 /* Now the current table should be allsubentries. */ 1955 /* Now the current table should be allsubentries. */
1835 if (dptr->newtable != PLAYTRACK) 1956 if (newtable != PLAYTRACK)
1836 { 1957 {
1837 tagtree_enter(tc); 1958 tagtree_enter(tc);
1838 tagtree_load(tc); 1959 tagtree_load(tc);
1839 dptr = tagtree_get_entry(tc, tc->selected_item); 1960 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
1840 1961
1841 /* And now the newtable should be playtrack. */ 1962 /* And now the newtable should be playtrack. */
1842 if (dptr->newtable != PLAYTRACK) 1963 if (newtable != PLAYTRACK)
1843 { 1964 {
1844 logf("newtable: %d !!", dptr->newtable); 1965 logf("newtable: %d !!", newtable);
1845 tc->dirlevel = dirlevel; 1966 tc->dirlevel = dirlevel;
1846 return false; 1967 return false;
1847 } 1968 }
@@ -1886,9 +2007,9 @@ static int tagtree_play_folder(struct tree_context* c)
1886 return 0; 2007 return 0;
1887} 2008}
1888 2009
1889struct tagentry* tagtree_get_entry(struct tree_context *c, int id) 2010static struct tagentry* tagtree_get_entry(struct tree_context *c, int id)
1890{ 2011{
1891 struct tagentry *entry = (struct tagentry *)c->cache.entries; 2012 struct tagentry *entry;
1892 int realid = id - current_offset; 2013 int realid = id - current_offset;
1893 2014
1894 /* Load the next chunk if necessary. */ 2015 /* Load the next chunk if necessary. */
@@ -1905,10 +2026,22 @@ struct tagentry* tagtree_get_entry(struct tree_context *c, int id)
1905 realid = id - current_offset; 2026 realid = id - current_offset;
1906 cpu_boost(false); 2027 cpu_boost(false);
1907 } 2028 }
1908 2029
2030 entry = get_entries(c);
1909 return &entry[realid]; 2031 return &entry[realid];
1910} 2032}
1911 2033
2034char* tagtree_get_entry_name(struct tree_context *c, int id,
2035 char* buf, size_t bufsize)
2036{
2037 struct tagentry *entry = tagtree_get_entry(c, id);
2038 if (!entry)
2039 return NULL;
2040 strlcpy(buf, entry->name, bufsize);
2041 return buf;
2042}
2043
2044
1912char *tagtree_get_title(struct tree_context* c) 2045char *tagtree_get_title(struct tree_context* c)
1913{ 2046{
1914 switch (c->currtable) 2047 switch (c->currtable)
diff --git a/apps/tagtree.h b/apps/tagtree.h
index aaf5158e5b..26952b40b7 100644
--- a/apps/tagtree.h
+++ b/apps/tagtree.h
@@ -30,19 +30,14 @@
30#define TAGMENU_MAX_MENUS 32 30#define TAGMENU_MAX_MENUS 32
31#define TAGMENU_MAX_FMTS 32 31#define TAGMENU_MAX_FMTS 32
32 32
33struct tagentry {
34 char *name;
35 int newtable;
36 int extraseek;
37};
38
39bool tagtree_export(void); 33bool tagtree_export(void);
40bool tagtree_import(void); 34bool tagtree_import(void);
41void tagtree_init(void) INIT_ATTR; 35void tagtree_init(void) INIT_ATTR;
42int tagtree_enter(struct tree_context* c); 36int tagtree_enter(struct tree_context* c);
43void tagtree_exit(struct tree_context* c); 37void tagtree_exit(struct tree_context* c);
44int tagtree_load(struct tree_context* c); 38int tagtree_load(struct tree_context* c);
45struct tagentry* tagtree_get_entry(struct tree_context *c, int id); 39char* tagtree_get_entry_name(struct tree_context *c, int id,
40 char* buf, size_t bufsize);
46bool tagtree_insert_selection_playlist(int position, bool queue); 41bool tagtree_insert_selection_playlist(int position, bool queue);
47char *tagtree_get_title(struct tree_context* c); 42char *tagtree_get_title(struct tree_context* c);
48int tagtree_get_attr(struct tree_context* c); 43int tagtree_get_attr(struct tree_context* c);
diff --git a/apps/tdspeed.c b/apps/tdspeed.c
index 476995a271..69699e5bb4 100644
--- a/apps/tdspeed.c
+++ b/apps/tdspeed.c
@@ -38,6 +38,46 @@
38 38
39#define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */ 39#define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */
40 40
41static int32_t** dsp_src;
42static int handles[4];
43static int32_t *overlap_buffer[2] = { NULL, NULL };
44static int32_t *outbuf[2] = { NULL, NULL };
45
46static int move_callback(int handle, void* current, void* new)
47{
48 /* TODO */
49 (void)handle;
50 if (dsp_src)
51 {
52 int ch = (current == outbuf[0]) ? 0 : 1;
53 dsp_src[ch] = outbuf[ch] = new;
54 }
55 return BUFLIB_CB_OK;
56}
57
58static struct buflib_callbacks ops = {
59 .move_callback = move_callback,
60 .shrink_callback = NULL,
61};
62
63static int ovl_move_callback(int handle, void* current, void* new)
64{
65 /* TODO */
66 (void)handle;
67 if (dsp_src)
68 {
69 int ch = (current == overlap_buffer[0]) ? 0 : 1;
70 overlap_buffer[ch] = new;
71 }
72 return BUFLIB_CB_OK;
73}
74
75static struct buflib_callbacks ovl_ops = {
76 .move_callback = ovl_move_callback,
77 .shrink_callback = NULL,
78};
79
80
41static struct tdspeed_state_s 81static struct tdspeed_state_s
42{ 82{
43 bool stereo; 83 bool stereo;
@@ -51,39 +91,47 @@ static struct tdspeed_state_s
51 int32_t *ovl_buff[2]; /* overlap buffer */ 91 int32_t *ovl_buff[2]; /* overlap buffer */
52} tdspeed_state; 92} tdspeed_state;
53 93
54static int32_t *overlap_buffer[2] = { NULL, NULL };
55static int32_t *outbuf[2] = { NULL, NULL };
56
57void tdspeed_init(void) 94void tdspeed_init(void)
58{ 95{
59 int handle;
60
61 if (!global_settings.timestretch_enabled) 96 if (!global_settings.timestretch_enabled)
62 return; 97 return;
63 98
64 /* Allocate buffers */ 99 /* Allocate buffers */
65 if (overlap_buffer[0] == NULL) 100 if (overlap_buffer[0] == NULL)
66 { 101 {
67 handle = core_alloc("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t)); 102 handles[0] = core_alloc_ex("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops);
68 overlap_buffer[0] = core_get_data(handle); 103 overlap_buffer[0] = core_get_data(handles[0]);
69 } 104 }
70 if (overlap_buffer[1] == NULL) 105 if (overlap_buffer[1] == NULL)
71 { 106 {
72 handle = core_alloc("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t)); 107 handles[1] = core_alloc_ex("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops);
73 overlap_buffer[1] = core_get_data(handle); 108 overlap_buffer[1] = core_get_data(handles[1]);
74 } 109 }
75 if (outbuf[0] == NULL) 110 if (outbuf[0] == NULL)
76 { 111 {
77 handle = core_alloc("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t)); 112 handles[2] = core_alloc_ex("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops);
78 outbuf[0] = core_get_data(handle); 113 outbuf[0] = core_get_data(handles[2]);
79 } 114 }
80 if (outbuf[1] == NULL) 115 if (outbuf[1] == NULL)
81 { 116 {
82 handle = core_alloc("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t)); 117 handles[3] = core_alloc_ex("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops);
83 outbuf[1] = core_get_data(handle); 118 outbuf[1] = core_get_data(handles[3]);
84 } 119 }
85} 120}
86 121
122void tdspeed_finish(void)
123{
124 for(unsigned i = 0; i < ARRAYLEN(handles); i++)
125 {
126 if (handles[i] > 0)
127 {
128 core_free(handles[i]);
129 handles[i] = 0;
130 }
131 }
132 overlap_buffer[0] = overlap_buffer[1] = NULL;
133 outbuf[0] = outbuf[1] = NULL;
134}
87 135
88bool tdspeed_config(int samplerate, bool stereo, int32_t factor) 136bool tdspeed_config(int samplerate, bool stereo, int32_t factor)
89{ 137{
@@ -390,6 +438,7 @@ long tdspeed_est_input_size(long size)
390 438
391int tdspeed_doit(int32_t *src[], int count) 439int tdspeed_doit(int32_t *src[], int count)
392{ 440{
441 dsp_src = src;
393 count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] }, 442 count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] },
394 src, count, 0, TDSPEED_OUTBUFSIZE); 443 src, count, 0, TDSPEED_OUTBUFSIZE);
395 444
diff --git a/apps/tdspeed.h b/apps/tdspeed.h
index c3b7fc4635..e91eeb1701 100644
--- a/apps/tdspeed.h
+++ b/apps/tdspeed.h
@@ -36,7 +36,8 @@
36#define GET_STRETCH(pitch, speed) \ 36#define GET_STRETCH(pitch, speed) \
37 ((speed * PITCH_SPEED_100 + pitch / 2L) / pitch) 37 ((speed * PITCH_SPEED_100 + pitch / 2L) / pitch)
38 38
39void tdspeed_init(void) INIT_ATTR; 39void tdspeed_init(void);
40void tdspeed_finish(void);
40bool tdspeed_config(int samplerate, bool stereo, int32_t factor); 41bool tdspeed_config(int samplerate, bool stereo, int32_t factor);
41long tdspeed_est_output_size(void); 42long tdspeed_est_output_size(void);
42long tdspeed_est_input_size(long size); 43long tdspeed_est_input_size(long size);
diff --git a/apps/tree.c b/apps/tree.c
index 211ddb2f9b..c7484ff420 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -104,12 +104,18 @@ static int ft_play_dirname(char* name);
104static void ft_play_filename(char *dir, char *file); 104static void ft_play_filename(char *dir, char *file);
105static void say_filetype(int attr); 105static void say_filetype(int attr);
106 106
107static struct entry* get_entry_at(struct tree_context *t, int index) 107struct entry* tree_get_entries(struct tree_context *t)
108{ 108{
109 struct entry* entries = t->cache.entries; 109 return core_get_data(t->cache.entries_handle);
110}
111
112struct entry* tree_get_entry_at(struct tree_context *t, int index)
113{
114 struct entry* entries = tree_get_entries(t);
110 return &entries[index]; 115 return &entries[index];
111} 116}
112 117
118
113static const char* tree_get_filename(int selected_item, void *data, 119static const char* tree_get_filename(int selected_item, void *data,
114 char *buffer, size_t buffer_len) 120 char *buffer, size_t buffer_len)
115{ 121{
@@ -122,12 +128,12 @@ static const char* tree_get_filename(int selected_item, void *data,
122 128
123 if (id3db) 129 if (id3db)
124 { 130 {
125 return tagtree_get_entry(&tc, selected_item)->name; 131 return tagtree_get_entry_name(&tc, selected_item, buffer, buffer_len);
126 } 132 }
127 else 133 else
128#endif 134#endif
129 { 135 {
130 struct entry* e = get_entry_at(local_tc, selected_item); 136 struct entry* e = tree_get_entry_at(local_tc, selected_item);
131 name = e->name; 137 name = e->name;
132 attr = e->attr; 138 attr = e->attr;
133 } 139 }
@@ -169,7 +175,7 @@ static int tree_get_filecolor(int selected_item, void * data)
169 if (*tc.dirfilter == SHOW_ID3DB) 175 if (*tc.dirfilter == SHOW_ID3DB)
170 return -1; 176 return -1;
171 struct tree_context * local_tc=(struct tree_context *)data; 177 struct tree_context * local_tc=(struct tree_context *)data;
172 struct entry* e = get_entry_at(local_tc, selected_item); 178 struct entry* e = tree_get_entry_at(local_tc, selected_item);
173 return filetype_get_color(e->name, e->attr); 179 return filetype_get_color(e->name, e->attr);
174} 180}
175#endif 181#endif
@@ -185,7 +191,7 @@ static enum themable_icons tree_get_fileicon(int selected_item, void * data)
185 else 191 else
186#endif 192#endif
187 { 193 {
188 struct entry* e = get_entry_at(local_tc, selected_item); 194 struct entry* e = tree_get_entry_at(local_tc, selected_item);
189 return filetype_get_icon(e->attr); 195 return filetype_get_icon(e->attr);
190 } 196 }
191} 197}
@@ -197,16 +203,17 @@ static int tree_voice_cb(int selected_item, void * data)
197 int attr=0; 203 int attr=0;
198#ifdef HAVE_TAGCACHE 204#ifdef HAVE_TAGCACHE
199 bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB; 205 bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB;
206 char buf[AVERAGE_FILENAME_LENGTH*2];
200 207
201 if (id3db) 208 if (id3db)
202 { 209 {
203 attr = tagtree_get_attr(local_tc); 210 attr = tagtree_get_attr(local_tc);
204 name = tagtree_get_entry(local_tc, selected_item)->name; 211 name = tagtree_get_entry_name(local_tc, selected_item, buf, sizeof(buf));
205 } 212 }
206 else 213 else
207#endif 214#endif
208 { 215 {
209 struct entry* e = get_entry_at(local_tc, selected_item); 216 struct entry* e = tree_get_entry_at(local_tc, selected_item);
210 name = e->name; 217 name = e->name;
211 attr = e->attr; 218 attr = e->attr;
212 } 219 }
@@ -329,7 +336,7 @@ static int tree_get_file_position(char * filename)
329 /* use lastfile to determine the selected item (default=0) */ 336 /* use lastfile to determine the selected item (default=0) */
330 for (i=0; i < tc.filesindir; i++) 337 for (i=0; i < tc.filesindir; i++)
331 { 338 {
332 e = get_entry_at(&tc, i); 339 e = tree_get_entry_at(&tc, i);
333 if (!strcasecmp(e->name, filename)) 340 if (!strcasecmp(e->name, filename))
334 return(i); 341 return(i);
335 } 342 }
@@ -531,7 +538,7 @@ char* get_current_file(char* buffer, size_t buffer_len)
531 return NULL; 538 return NULL;
532#endif 539#endif
533 540
534 struct entry* e = get_entry_at(&tc, tc.selected_item); 541 struct entry* e = tree_get_entry_at(&tc, tc.selected_item);
535 if (getcwd(buffer, buffer_len)) 542 if (getcwd(buffer, buffer_len))
536 { 543 {
537 if (tc.dirlength) 544 if (tc.dirlength)
@@ -650,7 +657,6 @@ static int dirbrowse(void)
650 657
651 gui_synclist_draw(&tree_lists); 658 gui_synclist_draw(&tree_lists);
652 while(1) { 659 while(1) {
653 struct entry *entries = tc.cache.entries;
654 bool restore = false; 660 bool restore = false;
655 if (tc.dirlevel < 0) 661 if (tc.dirlevel < 0)
656 tc.dirlevel = 0; /* shouldnt be needed.. this code needs work! */ 662 tc.dirlevel = 0; /* shouldnt be needed.. this code needs work! */
@@ -666,8 +672,9 @@ static int dirbrowse(void)
666 if ( numentries == 0 ) 672 if ( numentries == 0 )
667 break; 673 break;
668 674
675 short attr = tree_get_entry_at(&tc, tc.selected_item)->attr;
669 if ((tc.browse->flags & BROWSE_SELECTONLY) && 676 if ((tc.browse->flags & BROWSE_SELECTONLY) &&
670 !(entries[tc.selected_item].attr & ATTR_DIRECTORY)) 677 !(attr & ATTR_DIRECTORY))
671 { 678 {
672 tc.browse->flags |= BROWSE_SELECTED; 679 tc.browse->flags |= BROWSE_SELECTED;
673 get_current_file(tc.browse->buf, tc.browse->bufsize); 680 get_current_file(tc.browse->buf, tc.browse->bufsize);
@@ -792,15 +799,14 @@ static int dirbrowse(void)
792 else 799 else
793#endif 800#endif
794 { 801 {
795 attr = entries[tc.selected_item].attr; 802 struct entry *entry = tree_get_entry_at(&tc, tc.selected_item);
803 attr = entry->attr;
796 804
797 if (currdir[1]) /* Not in / */ 805 if (currdir[1]) /* Not in / */
798 snprintf(buf, sizeof buf, "%s/%s", 806 snprintf(buf, sizeof buf, "%s/%s",
799 currdir, 807 currdir, entry->name);
800 entries[tc.selected_item].name);
801 else /* In / */ 808 else /* In / */
802 snprintf(buf, sizeof buf, "/%s", 809 snprintf(buf, sizeof buf, "/%s", entry->name);
803 entries[tc.selected_item].name);
804 } 810 }
805 onplay_result = onplay(buf, attr, curr_context, hotkey); 811 onplay_result = onplay(buf, attr, curr_context, hotkey);
806 } 812 }
@@ -999,10 +1005,36 @@ int rockbox_browse(struct browse_context *browse)
999 return ret_val; 1005 return ret_val;
1000} 1006}
1001 1007
1008static int move_callback(int handle, void* current, void* new)
1009{
1010 struct tree_cache* cache = &tc.cache;
1011 if (cache->lock_count > 0)
1012 return BUFLIB_CB_CANNOT_MOVE;
1013
1014 size_t diff = new - current;
1015 /* FIX_PTR makes sure to not accidentally update static allocations */
1016#define FIX_PTR(x) \
1017 { if ((void*)x > current && (void*)x < (current+cache->name_buffer_size)) x+= diff; }
1018
1019 if (handle == cache->name_buffer_handle)
1020 { /* update entry structs, *even if they are struct tagentry */
1021 struct entry *this = core_get_data(cache->entries_handle);
1022 struct entry *last = this + cache->max_entries;
1023 for(; this < last; this++)
1024 FIX_PTR(this->name);
1025 }
1026 /* nothing to do if entries moved */
1027 return BUFLIB_CB_OK;
1028}
1029
1030static struct buflib_callbacks ops = {
1031 .move_callback = move_callback,
1032 .shrink_callback = NULL,
1033};
1034
1002void tree_mem_init(void) 1035void tree_mem_init(void)
1003{ 1036{
1004 /* initialize tree context struct */ 1037 /* initialize tree context struct */
1005 int handle;
1006 struct tree_cache* cache = &tc.cache; 1038 struct tree_cache* cache = &tc.cache;
1007 memset(&tc, 0, sizeof(tc)); 1039 memset(&tc, 0, sizeof(tc));
1008 tc.dirfilter = &global_settings.dirfilter; 1040 tc.dirfilter = &global_settings.dirfilter;
@@ -1010,12 +1042,14 @@ void tree_mem_init(void)
1010 1042
1011 cache->name_buffer_size = AVERAGE_FILENAME_LENGTH * 1043 cache->name_buffer_size = AVERAGE_FILENAME_LENGTH *
1012 global_settings.max_files_in_dir; 1044 global_settings.max_files_in_dir;
1013 handle = core_alloc("tree names", cache->name_buffer_size); 1045 cache->name_buffer_handle = core_alloc_ex("tree names",
1014 cache->name_buffer = core_get_data(handle); 1046 cache->name_buffer_size,
1047 &ops);
1015 1048
1016 cache->max_entries = global_settings.max_files_in_dir; 1049 cache->max_entries = global_settings.max_files_in_dir;
1017 handle = core_alloc("tree entries", cache->max_entries*(sizeof(struct entry))); 1050 cache->entries_handle = core_alloc_ex("tree entries",
1018 cache->entries = core_get_data(handle); 1051 cache->max_entries*(sizeof(struct entry)),
1052 &ops);
1019 tree_get_filetypes(&filetypes, &filetypes_count); 1053 tree_get_filetypes(&filetypes, &filetypes_count);
1020} 1054}
1021 1055
diff --git a/apps/tree.h b/apps/tree.h
index c07b92f298..2b296050d3 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -26,26 +26,30 @@
26#include <file.h> 26#include <file.h>
27#include "icon.h" 27#include "icon.h"
28 28
29/* keep this struct compatible (total size and name member)
30 * with struct tagtree_entry (tagtree.h) */
29struct entry { 31struct entry {
30 short attr; /* FAT attributes + file type flags */
31 unsigned long time_write; /* Last write time */
32 char *name; 32 char *name;
33 int attr; /* FAT attributes + file type flags */
34 unsigned time_write; /* Last write time */
33}; 35};
34 36
35
36#define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */ 37#define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */
37#define BROWSE_NO_CONTEXT_MENU 0x0002 /* disable context menu */ 38#define BROWSE_NO_CONTEXT_MENU 0x0002 /* disable context menu */
38#define BROWSE_SELECTED 0x0100 /* this bit is set if user selected item */ 39#define BROWSE_SELECTED 0x0100 /* this bit is set if user selected item */
39 40
40struct tree_context; 41struct tree_context;
42
41struct tree_cache { 43struct tree_cache {
42 /* A big buffer with plenty of entry structs, 44 /* A big buffer with plenty of entry structs, contains all files and dirs
43 * contains all files and dirs in the current 45 * in the current dir (with filters applied)
44 * dir (with filters applied) */ 46 * Note that they're buflib-allocated and can therefore possibly move
45 void* entries; 47 * They need to be locked if used around yielding functions */
46 char* name_buffer; 48 int entries_handle; /* handle to the entry cache */
47 int max_entries; /* Max entries in the cache */ 49 int name_buffer_handle; /* handle to the name cache */
48 int name_buffer_size; /* in bytes */ 50 int max_entries; /* Max entries in the cache */
51 int name_buffer_size; /* in bytes */
52 volatile int lock_count; /* non-0 if buffers may not move */
49}; 53};
50 54
51struct browse_context { 55struct browse_context {
@@ -95,6 +99,10 @@ struct tree_context {
95 struct browse_context *browse; 99 struct browse_context *browse;
96}; 100};
97 101
102/*
103 * Call one of the two below after yields since the entrys may move inbetween */
104struct entry* tree_get_entries(struct tree_context *t);
105struct entry* tree_get_entry_at(struct tree_context *t, int index);
98void tree_drawlists(void); 106void tree_drawlists(void);
99void tree_mem_init(void) INIT_ATTR; 107void tree_mem_init(void) INIT_ATTR;
100void tree_gui_init(void) INIT_ATTR; 108void tree_gui_init(void) INIT_ATTR;
@@ -108,6 +116,14 @@ void browse_context_init(struct browse_context *browse,
108int rockbox_browse(struct browse_context *browse); 116int rockbox_browse(struct browse_context *browse);
109bool create_playlist(void); 117bool create_playlist(void);
110void resume_directory(const char *dir); 118void resume_directory(const char *dir);
119static inline void tree_lock_cache(struct tree_context *t)
120{
121 t->cache.lock_count++;
122}
123static inline void tree_unlock_cache(struct tree_context *t)
124{
125 t->cache.lock_count--;
126}
111#ifdef WIN32 127#ifdef WIN32
112/* it takes an int on windows */ 128/* it takes an int on windows */
113#define getcwd_size_t int 129#define getcwd_size_t int
diff --git a/firmware/buflib.c b/firmware/buflib.c
index 51cf86bf5b..880357ccf4 100644
--- a/firmware/buflib.c
+++ b/firmware/buflib.c
@@ -192,10 +192,6 @@ handle_table_shrink(struct buflib_context *ctx)
192static bool 192static bool
193move_block(struct buflib_context* ctx, union buflib_data* block, int shift) 193move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
194{ 194{
195#if 1 /* moving temporarily disabled */
196 (void)ctx;(void)block;(void)shift;
197 return false;
198#else
199 char* new_start; 195 char* new_start;
200 union buflib_data *new_block, *tmp = block[1].handle; 196 union buflib_data *new_block, *tmp = block[1].handle;
201 struct buflib_callbacks *ops = block[2].ops; 197 struct buflib_callbacks *ops = block[2].ops;
@@ -218,7 +214,6 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
218 memmove(new_block, block, block->val * sizeof(union buflib_data)); 214 memmove(new_block, block, block->val * sizeof(union buflib_data));
219 215
220 return true; 216 return true;
221#endif
222} 217}
223 218
224/* Compact allocations and handle table, adjusting handle pointers as needed. 219/* Compact allocations and handle table, adjusting handle pointers as needed.
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index 334801ce57..6b2260def3 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -66,12 +66,21 @@ struct fdbind_queue {
66 int fd; 66 int fd;
67}; 67};
68 68
69/* Exported structures. */ 69/* Unions with char to make pointer arithmetic simpler and avoid casting */
70struct dircache_entry { 70struct dircache_entry {
71 struct dirinfo info; 71 struct dirinfo info;
72 struct dircache_entry *next; 72 union {
73 struct dircache_entry *up; 73 struct dircache_entry *next;
74 struct dircache_entry *down; 74 char* next_char;
75 };
76 union {
77 struct dircache_entry *up;
78 char* up_char;
79 };
80 union {
81 struct dircache_entry *down;
82 char* down_char;
83 };
75 long startcluster; 84 long startcluster;
76 char *d_name; 85 char *d_name;
77}; 86};
@@ -130,6 +139,44 @@ static inline struct dircache_entry* get_entry(int id)
130 return &dircache_root[id]; 139 return &dircache_root[id];
131} 140}
132 141
142/* flag to make sure buffer doesn't move due to other allocs.
143 * this is set to true completely during dircache build */
144static bool dont_move = false;
145static int dircache_handle;
146static int move_callback(int handle, void* current, void* new)
147{
148 (void)handle;
149 if (dont_move)
150 return BUFLIB_CB_CANNOT_MOVE;
151
152 /* relocate the cache */
153 ptrdiff_t diff = new - current;
154 for(unsigned i = 0; i < entry_count; i++)
155 {
156 if (dircache_root[i].d_name)
157 dircache_root[i].d_name += diff;
158 if (dircache_root[i].next_char)
159 dircache_root[i].next_char += diff;
160 if (dircache_root[i].up_char)
161 dircache_root[i].up_char += diff;
162 if (dircache_root[i].down_char)
163 dircache_root[i].down_char += diff;
164 }
165 dircache_root = new;
166
167 d_names_start -= diff;
168 d_names_end -= diff;
169 dot -= diff;
170 dotdot -= diff;
171
172 return BUFLIB_CB_OK;
173}
174
175static struct buflib_callbacks ops = {
176 .move_callback = move_callback,
177 .shrink_callback = NULL,
178};
179
133#ifdef HAVE_EEPROM_SETTINGS 180#ifdef HAVE_EEPROM_SETTINGS
134/** 181/**
135 * Open the dircache file to save a snapshot on disk 182 * Open the dircache file to save a snapshot on disk
@@ -573,10 +620,11 @@ int dircache_load(void)
573 } 620 }
574 621
575 allocated_size = maindata.size + DIRCACHE_RESERVE; 622 allocated_size = maindata.size + DIRCACHE_RESERVE;
576 int handle = core_alloc("dircache", allocated_size); 623 dircache_handle = core_alloc_ex("dircache", allocated_size, &ops);
577 dircache_root = core_get_data(handle); 624 /* block movement during upcoming I/O */
578 /* needs to be struct-size aligned so that the pointer arithmetic below works */ 625 dont_move = true;
579 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); 626 dircache_root = core_get_data(dircache_handle);
627 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry*));
580 entry_count = maindata.entry_count; 628 entry_count = maindata.entry_count;
581 appflags = maindata.appflags; 629 appflags = maindata.appflags;
582 630
@@ -608,8 +656,9 @@ int dircache_load(void)
608 dotdot = dot - sizeof(".."); 656 dotdot = dot - sizeof("..");
609 657
610 /* d_names are in reverse order, so the last entry points to the first string */ 658 /* d_names are in reverse order, so the last entry points to the first string */
611 ptrdiff_t offset_d_names = maindata.d_names_start - d_names_start, 659 ptrdiff_t offset_d_names = maindata.d_names_start - d_names_start;
612 offset_entries = maindata.root_entry - dircache_root; 660 ptrdiff_t offset_entries = maindata.root_entry - dircache_root;
661 offset_entries *= sizeof(struct dircache_entry); /* make it bytes */
613 662
614 /* offset_entries is less likely to differ, so check if it's 0 in the loop 663 /* offset_entries is less likely to differ, so check if it's 0 in the loop
615 * offset_d_names however is almost always non-zero, since dircache_save() 664 * offset_d_names however is almost always non-zero, since dircache_save()
@@ -625,12 +674,12 @@ int dircache_load(void)
625 674
626 if (offset_entries == 0) 675 if (offset_entries == 0)
627 continue; 676 continue;
628 if (dircache_root[i].next) 677 if (dircache_root[i].next_char)
629 dircache_root[i].next -= offset_entries; 678 dircache_root[i].next_char -= offset_entries;
630 if (dircache_root[i].up) 679 if (dircache_root[i].up_char)
631 dircache_root[i].up -= offset_entries; 680 dircache_root[i].up_char -= offset_entries;
632 if (dircache_root[i].down) 681 if (dircache_root[i].down_char)
633 dircache_root[i].down -= offset_entries; 682 dircache_root[i].down_char -= offset_entries;
634 } 683 }
635 } 684 }
636 685
@@ -640,6 +689,7 @@ int dircache_load(void)
640 logf("Done, %ld KiB used", dircache_size / 1024); 689 logf("Done, %ld KiB used", dircache_size / 1024);
641 dircache_initialized = true; 690 dircache_initialized = true;
642 memset(fd_bindings, 0, sizeof(fd_bindings)); 691 memset(fd_bindings, 0, sizeof(fd_bindings));
692 dont_move = false;
643 693
644 return 0; 694 return 0;
645} 695}
@@ -660,6 +710,7 @@ int dircache_save(void)
660 return -1; 710 return -1;
661 711
662 logf("Saving directory cache"); 712 logf("Saving directory cache");
713 dont_move = true;
663 fd = open_dircache_file(O_WRONLY | O_CREAT | O_TRUNC, 0666); 714 fd = open_dircache_file(O_WRONLY | O_CREAT | O_TRUNC, 0666);
664 715
665 maindata.magic = DIRCACHE_MAGIC; 716 maindata.magic = DIRCACHE_MAGIC;
@@ -698,7 +749,7 @@ int dircache_save(void)
698 return -4; 749 return -4;
699 } 750 }
700 751
701 752 dont_move = false;
702 return 0; 753 return 0;
703} 754}
704#endif /* HAVE_EEPROM_SETTINGS */ 755#endif /* HAVE_EEPROM_SETTINGS */
@@ -720,6 +771,7 @@ static int dircache_do_rebuild(void)
720 /* reset dircache and alloc root entry */ 771 /* reset dircache and alloc root entry */
721 entry_count = 0; 772 entry_count = 0;
722 root_entry = allocate_entry(); 773 root_entry = allocate_entry();
774 dont_move = true;
723 775
724#ifdef HAVE_MULTIVOLUME 776#ifdef HAVE_MULTIVOLUME
725 append_position = root_entry; 777 append_position = root_entry;
@@ -740,6 +792,7 @@ static int dircache_do_rebuild(void)
740 cpu_boost(false); 792 cpu_boost(false);
741 dircache_size = 0; 793 dircache_size = 0;
742 dircache_initializing = false; 794 dircache_initializing = false;
795 dont_move = false;
743 return -2; 796 return -2;
744 } 797 }
745 cpu_boost(false); 798 cpu_boost(false);
@@ -765,7 +818,8 @@ static int dircache_do_rebuild(void)
765 if (allocated_size - dircache_size < DIRCACHE_RESERVE) 818 if (allocated_size - dircache_size < DIRCACHE_RESERVE)
766 reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size); 819 reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size);
767 } 820 }
768 821
822 dont_move = false;
769 return 1; 823 return 1;
770} 824}
771 825
@@ -790,7 +844,8 @@ static void dircache_thread(void)
790#endif 844#endif
791 case DIRCACHE_BUILD: 845 case DIRCACHE_BUILD:
792 thread_enabled = true; 846 thread_enabled = true;
793 dircache_do_rebuild(); 847 if (dircache_do_rebuild() < 0)
848 core_free(dircache_handle);
794 thread_enabled = false; 849 thread_enabled = false;
795 break ; 850 break ;
796 851
@@ -848,11 +903,10 @@ int dircache_build(int last_size)
848 903
849 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT ) 904 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT )
850 { 905 {
851 int handle;
852 allocated_size = last_size + DIRCACHE_RESERVE; 906 allocated_size = last_size + DIRCACHE_RESERVE;
853 handle = core_alloc("dircache", allocated_size); 907 dircache_handle = core_alloc_ex("dircache", allocated_size, &ops);
854 dircache_root = core_get_data(handle); 908 dircache_root = core_get_data(dircache_handle);
855 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); 909 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry*));
856 d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1; 910 d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1;
857 dircache_size = 0; 911 dircache_size = 0;
858 thread_enabled = true; 912 thread_enabled = true;
@@ -869,10 +923,10 @@ int dircache_build(int last_size)
869 * after generation the buffer will be compacted with DIRCACHE_RESERVE 923 * after generation the buffer will be compacted with DIRCACHE_RESERVE
870 * free bytes inbetween */ 924 * free bytes inbetween */
871 size_t got_size; 925 size_t got_size;
872 int handle = core_alloc_maximum("dircache", &got_size, NULL); 926 dircache_handle = core_alloc_maximum("dircache", &got_size, &ops);
873 char* buf = core_get_data(handle); 927 char* buf = core_get_data(dircache_handle);
874 dircache_root = (struct dircache_entry*)ALIGN_UP(buf, 928 dircache_root = (struct dircache_entry*)ALIGN_UP(buf,
875 sizeof(struct dircache_entry)); 929 sizeof(struct dircache_entry*));
876 d_names_start = d_names_end = buf + got_size - 1; 930 d_names_start = d_names_end = buf + got_size - 1;
877 dircache_size = 0; 931 dircache_size = 0;
878 generate_dot_d_names(); 932 generate_dot_d_names();
@@ -909,11 +963,11 @@ int dircache_build(int last_size)
909 allocated_size = (d_names_end - buf); 963 allocated_size = (d_names_end - buf);
910 reserve_used = 0; 964 reserve_used = 0;
911 965
912 core_shrink(handle, dircache_root, allocated_size); 966 core_shrink(dircache_handle, dircache_root, allocated_size);
913 return res; 967 return res;
914fail: 968fail:
915 dircache_disable(); 969 dircache_disable();
916 core_free(handle); 970 core_free(dircache_handle);
917 return res; 971 return res;
918} 972}
919 973
@@ -928,7 +982,9 @@ void* dircache_steal_buffer(size_t *size)
928 *size = 0; 982 *size = 0;
929 return NULL; 983 return NULL;
930 } 984 }
931 985
986 /* since we give up the buffer (without freeing), it must not move anymore */
987 dont_move = true;
932 *size = dircache_size + (DIRCACHE_RESERVE-reserve_used); 988 *size = dircache_size + (DIRCACHE_RESERVE-reserve_used);
933 989
934 return dircache_root; 990 return dircache_root;
diff --git a/firmware/core_alloc.c b/firmware/core_alloc.c
index 75dfc75b86..2250f5c664 100644
--- a/firmware/core_alloc.c
+++ b/firmware/core_alloc.c
@@ -6,7 +6,6 @@
6 6
7/* not static so it can be discovered by core_get_data() */ 7/* not static so it can be discovered by core_get_data() */
8struct buflib_context core_ctx; 8struct buflib_context core_ctx;
9
10void core_allocator_init(void) 9void core_allocator_init(void)
11{ 10{
12 buffer_init(); 11 buffer_init();