From 9d4689e5cc9830fb0382474ed22da528e4826d19 Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Mon, 15 Aug 2011 17:54:16 +0200 Subject: [PATCH] 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. --- apps/debug_menu.c | 33 ++++- apps/dsp.c | 60 ++++++--- apps/filetree.c | 31 +++-- apps/filetypes.c | 22 +++- apps/main.c | 6 - apps/menus/playback_menu.c | 2 +- apps/mpeg.c | 124 +++++++++++++----- apps/playback.c | 82 ++++++++++-- apps/playlist.c | 95 +++++++++----- apps/playlist.h | 9 +- apps/plugin.c | 6 + apps/plugin.h | 2 + apps/plugins/imageviewer/imageviewer.c | 2 +- apps/plugins/mikmod/mikmod.c | 2 +- apps/plugins/mpegplayer/mpegplayer.c | 2 +- apps/plugins/rockpaint.c | 2 +- apps/scrobbler.c | 10 +- apps/tagcache.c | 144 +++++++++++++++------ apps/tagcache.h | 4 +- apps/tagtree.c | 229 ++++++++++++++++++++++++++------- apps/tagtree.h | 9 +- apps/talk.c | 26 +++- apps/tdspeed.c | 67 ++++++++-- apps/tdspeed.h | 3 +- apps/tree.c | 78 +++++++---- apps/tree.h | 36 ++++-- firmware/buflib.c | 5 - firmware/common/dircache.c | 114 +++++++++++----- firmware/core_alloc.c | 14 +- firmware/include/core_alloc.h | 1 + 30 files changed, 919 insertions(+), 301 deletions(-) diff --git a/apps/debug_menu.c b/apps/debug_menu.c index fb8575ec6..e7142190d 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -425,11 +425,42 @@ static const char* bf_getname(int selected_item, void *data, return buffer; } +static int bf_action_cb(int action, struct gui_synclist* list) +{ + int* has_freed = list->data; + if (action == ACTION_STD_OK) + { + int pos = gui_synclist_get_sel_pos(list); + if (pos == 0 && !*has_freed) /* first item */ + { + splash(HZ/2, "Freeing debug handle"); + free_debug(); + *has_freed = 1; + } + else + { + splash(HZ/1, "Attempting a 64k allocation"); + int handle = core_alloc("test", 64<<10); + splash(HZ/2, (handle > 0) ? "Success":"Fail"); + if (handle > 0) + core_free(handle); + simplelist_set_line_count(core_get_num_blocks()); + } + action = ACTION_REDRAW; + } + else if (action == ACTION_NONE) + action = ACTION_REDRAW; + return action; +} + static bool dbg_buflib_allocs(void) { + int has_freed = 0; struct simplelist_info info; - simplelist_info_init(&info, "mem allocs", core_get_num_blocks(), NULL); + simplelist_info_init(&info, "mem allocs", core_get_num_blocks(), &has_freed); info.get_name = bf_getname; + info.action_callback = bf_action_cb; + info.timeout = HZ/2; return simplelist_show_list(&info); } diff --git a/apps/dsp.c b/apps/dsp.c index a728dd75e..167c04342 100644 --- a/apps/dsp.c +++ b/apps/dsp.c @@ -318,30 +318,50 @@ static void tdspeed_setup(struct dsp_config *dspc) resample_buf = big_resample_buf; } + +static int move_callback(int handle, void* current, void* new) +{ + /* TODO */ + (void)handle;(void)current;; + big_sample_buf = new; + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; + + void dsp_timestretch_enable(bool enabled) { /* Hook to set up timestretch buffer on first call to settings_apply() */ - if (big_sample_buf_count < 0) /* Only do something on first call */ + static int handle; + if (enabled) { - if (enabled) - { - int handle; - /* Set up timestretch buffers */ - big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO; - big_sample_buf = small_resample_buf; - handle = core_alloc("resample buf", - big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t)); - if (handle > 0) - big_resample_buf = core_get_data(handle); - else - big_sample_buf_count = 0; + if (big_sample_buf_count > 0) + return; /* already allocated and enabled */ + /* Set up timestretch buffers */ + big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO; + big_sample_buf = small_resample_buf; + handle = core_alloc_ex("resample buf", + big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t), &ops); + if (handle > 0) + { /* success, now setup tdspeed */ + big_resample_buf = core_get_data(handle); + tdspeed_init(); + tdspeed_setup(&AUDIO_DSP); } - else - { - /* Not enabled at startup, "big" buffers will never be available */ - big_sample_buf_count = 0; - } - tdspeed_setup(&AUDIO_DSP); + } + if (!enabled || (handle <= 0)) /* disable */ + { + dsp_set_timestretch(PITCH_SPEED_100); + tdspeed_finish(); + if (handle > 0) + core_free(handle); + handle = 0; + big_sample_buf = NULL; + big_sample_buf_count = 0; } } @@ -1211,7 +1231,7 @@ int dsp_callback(int msg, intptr_t param) */ int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count) { - int32_t *tmp[2]; + static int32_t *tmp[2]; /* tdspeed_doit() needs it static */ static long last_yield; long tick; int written = 0; diff --git a/apps/filetree.c b/apps/filetree.c index 1aee80b6b..35bb2a8fd 100644 --- a/apps/filetree.c +++ b/apps/filetree.c @@ -29,6 +29,7 @@ #include #include "bookmark.h" #include "tree.h" +#include "core_alloc.h" #include "settings.h" #include "filetypes.h" #include "talk.h" @@ -60,7 +61,8 @@ int ft_build_playlist(struct tree_context* c, int start_index) int i; int start=start_index; - struct entry *entries = c->cache.entries; + tree_lock_cache(c); + struct entry *entries = tree_get_entries(c); for(i = 0;i < c->filesindir;i++) { @@ -77,6 +79,8 @@ int ft_build_playlist(struct tree_context* c, int start_index) } } + tree_unlock_cache(c); + return start_index; } @@ -127,13 +131,15 @@ static void check_file_thumbnails(struct tree_context* c) { int i; struct dirent *entry; - struct entry* entries = c->cache.entries; + struct entry* entries; DIR *dir; dir = opendir(c->currdir); if(!dir) return; /* mark all files as non talking, except the .talk ones */ + entries = tree_get_entries(c); + tree_lock_cache(c); for (i=0; i < c->filesindir; i++) { if (entries[i].attr & ATTR_DIRECTORY) @@ -177,6 +183,7 @@ static void check_file_thumbnails(struct tree_context* c) } } } + tree_unlock_cache(c); closedir(dir); } @@ -287,11 +294,11 @@ int ft_load(struct tree_context* c, const char* tempdir) c->dirsindir = 0; c->dirfull = false; + tree_lock_cache(c); while ((entry = readdir(dir))) { int len; struct dirinfo info; - struct entry* table = c->cache.entries; - struct entry* dptr = &table[files_in_dir]; + struct entry* dptr = tree_get_entry_at(c, files_in_dir); if (!entry) break; @@ -369,7 +376,7 @@ int ft_load(struct tree_context* c, const char* tempdir) ++files_in_dir; - dptr->name = &c->cache.name_buffer[name_buffer_used]; + dptr->name = core_get_data(c->cache.name_buffer_handle)+name_buffer_used; dptr->time_write = (long)info.wrtdate<<16 | (long)info.wrttime; /* in one # */ @@ -384,13 +391,14 @@ int ft_load(struct tree_context* c, const char* tempdir) closedir(dir); compare_sort_dir = c->sort_dir; - qsort(c->cache.entries, files_in_dir, sizeof(struct entry), compare); + qsort(tree_get_entries(c), files_in_dir, sizeof(struct entry), compare); /* If thumbnail talking is enabled, make an extra run to mark files with associated thumbnails, so we don't do unsuccessful spinups later. */ if (global_settings.talk_file_clip) check_file_thumbnails(c); /* map .talk to ours */ + tree_unlock_cache(c); return 0; } #ifdef HAVE_LCD_BITMAP @@ -424,15 +432,15 @@ int ft_enter(struct tree_context* c) { int rc = GO_TO_PREVIOUS; char buf[MAX_PATH]; - struct entry* table = c->cache.entries; - struct entry *file = &table[c->selected_item]; + struct entry* file = tree_get_entry_at(c, c->selected_item); + int file_attr = file->attr; if (c->currdir[1]) snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name); else snprintf(buf,sizeof(buf),"/%s",file->name); - if (file->attr & ATTR_DIRECTORY) { + if (file_attr & ATTR_DIRECTORY) { memcpy(c->currdir, buf, sizeof(c->currdir)); if ( c->dirlevel < MAX_DIR_LEVELS ) c->selected_item_history[c->dirlevel] = c->selected_item; @@ -444,7 +452,7 @@ int ft_enter(struct tree_context* c) bool play = false; int start_index=0; - switch ( file->attr & FILE_ATTR_MASK ) { + switch ( file_attr & FILE_ATTR_MASK ) { case FILE_ATTR_M3U: if (!bookmark_autoload(buf)) playlist_viewer_ex(buf); @@ -612,7 +620,7 @@ int ft_enter(struct tree_context* c) char *plugin = buf, *argument = NULL, lua_path[MAX_PATH]; int ret; - if ((file->attr & FILE_ATTR_MASK) == FILE_ATTR_LUA) { + if ((file_attr & FILE_ATTR_MASK) == FILE_ATTR_LUA) { snprintf(lua_path, sizeof(lua_path)-1, "%s/lua.rock", VIEWERS_DIR); /* Use a #define here ? */ plugin = lua_path; argument = buf; @@ -658,6 +666,7 @@ int ft_enter(struct tree_context* c) break; } + struct entry* file = tree_get_entry_at(c, c->selected_item); plugin = filetype_get_plugin(file); if (plugin) { diff --git a/apps/filetypes.c b/apps/filetypes.c index c52c734a1..942ff329f 100644 --- a/apps/filetypes.c +++ b/apps/filetypes.c @@ -184,6 +184,26 @@ static unsigned char highest_attr = 0; static int viewer_count = 0; static int strdup_handle, strdup_bufsize, strdup_cur_idx; +static int move_callback(int handle, void* current, void* new) +{ + /*could compare to strdup_handle, but ops is only used once */ + (void)handle; + size_t diff = new - current; +#define FIX_PTR(x) \ + { if ((void*)x > current && (void*)x < (current+strdup_bufsize)) x+= diff; } + for(int i = 0; i < filetype_count; i++) + { + FIX_PTR(filetypes[i].extension); + FIX_PTR(filetypes[i].plugin); + } + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; + static char *filetypes_strdup(char* string) { char *buffer = core_get_data(strdup_handle) + strdup_cur_idx; @@ -323,7 +343,7 @@ void filetype_init(void) return; strdup_bufsize = filesize(fd); - strdup_handle = core_alloc("filetypes", strdup_bufsize); + strdup_handle = core_alloc_ex("filetypes", strdup_bufsize, &ops); if (strdup_handle <= 0) return; read_builtin_types(); diff --git a/apps/main.c b/apps/main.c index cc9c9e8d8..07a8bba44 100644 --- a/apps/main.c +++ b/apps/main.c @@ -403,9 +403,6 @@ static void init(void) #endif /* CONFIG_CODEC != SWCODEC */ scrobbler_init(); -#if CONFIG_CODEC == SWCODEC && defined (HAVE_PITCHSCREEN) - tdspeed_init(); -#endif /* CONFIG_CODEC == SWCODEC */ audio_init(); @@ -659,9 +656,6 @@ static void init(void) tree_mem_init(); filetype_init(); scrobbler_init(); -#if CONFIG_CODEC == SWCODEC && defined (HAVE_PITCHSCREEN) - tdspeed_init(); -#endif /* CONFIG_CODEC == SWCODEC */ theme_init_buffer(); #if CONFIG_CODEC != SWCODEC diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c index 1b1a13a6a..a219373a8 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 { case ACTION_EXIT_MENUITEM: /* on exit */ if (!scrobbler_is_enabled() && global_settings.audioscrobbler) - splash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); + scrobbler_init(); if(scrobbler_is_enabled() && !global_settings.audioscrobbler) scrobbler_shutdown(); diff --git a/apps/mpeg.c b/apps/mpeg.c index 014557487..8bc9aed95 100644 --- a/apps/mpeg.c +++ b/apps/mpeg.c @@ -39,7 +39,7 @@ #include "mp3_playback.h" #include "talk.h" #include "sound.h" -#include "bitswap.h" +#include "system.h" #include "appevents.h" #include "playlist.h" #include "cuesheet.h" @@ -140,6 +140,7 @@ static struct cuesheet *curr_cuesheet = NULL; static bool checked_for_cuesheet = false; static const char mpeg_thread_name[] = "mpeg"; +static unsigned int audio_thread_id; static unsigned int mpeg_errno; static bool playing = false; /* We are playing an MP3 stream */ @@ -492,20 +493,84 @@ unsigned long mpeg_get_last_header(void) #endif /* !SIMULATOR */ } -/* Buffer must not move. And not shrink for now */ -static struct buflib_callbacks ops = { NULL, NULL }; +static void do_stop(void) +{ + DEBUGF("MPEG_STOP\n"); + is_playing = false; + paused = false; + +#ifndef SIMULATOR + if (playing) + playlist_update_resume_info(audio_current_track()); + + stop_playing(); + mpeg_stop_done = true; +#else + playing = false; +#endif +} + +static void audio_reset_buffer_noalloc(void* buf, size_t bufsize); +/* Buffer must not move. */ +static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size) +{ + DEBUGF("%s()\n", __func__); + + long offset = audio_current_track()->offset; + int status = audio_status(); + /* TODO: Do it without stopping playback, if possible */ + /* don't call audio_hard_stop() as it frees this handle */ + if (thread_self() == audio_thread_id) + { /* inline case MPEG_STOP (audio_stop()) response + * if we're in the audio thread since audio_stop() otherwise deadlocks */ + do_stop(); + } + else + audio_stop(); + talk_buffer_steal(); /* we obtain control over the buffer */ + + /* we should be free to change the buffer now */ + size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); + ssize_t size = (ssize_t)old_size - wanted_size; + switch (hints & BUFLIB_SHRINK_POS_MASK) + { + case BUFLIB_SHRINK_POS_BACK: + core_shrink(handle, start, size); + audio_reset_buffer_noalloc(start, size); + break; + case BUFLIB_SHRINK_POS_FRONT: + core_shrink(handle, start + wanted_size, size); + audio_reset_buffer_noalloc(start + wanted_size, size); + break; + } + if (!(status & AUDIO_STATUS_PAUSE)) + { /* safe to call even from the audio thread (due to queue_post()) */ + audio_play(offset); + } + + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = NULL, + .shrink_callback = shrink_callback, +}; + unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) { (void)talk_buf; /* always grab the voice buffer for now */ if (buffer_size) /* special case for talk_init() */ + audio_hard_stop(); + + if (!audiobuf_handle) { size_t bufsize; - audio_hard_stop(); /* audio_hard_stop() frees audiobuf, so re-aquire */ audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); audiobuflen = bufsize; - *buffer_size = audiobuflen; + if (buffer_size) + *buffer_size = audiobuflen; } mpeg_audiobuf = core_get_data(audiobuf_handle); @@ -1314,15 +1379,7 @@ static void mpeg_thread(void) break; case MPEG_STOP: - DEBUGF("MPEG_STOP\n"); - is_playing = false; - paused = false; - - if (playing) - playlist_update_resume_info(audio_current_track()); - - stop_playing(); - mpeg_stop_done = true; + do_stop(); break; case MPEG_PAUSE: @@ -2679,19 +2736,29 @@ size_t audio_buffer_available(void) return core_available(); } -static void audio_reset_buffer(void) +static void audio_reset_buffer_noalloc(void* buf, size_t bufsize) { talk_buffer_steal(); /* will use the mp3 buffer */ + mpeg_audiobuf = buf; + audiobuflen = bufsize; + if (global_settings.cuesheet) + { /* enable cuesheet support */ + curr_cuesheet = (struct cuesheet*)mpeg_audiobuf; + mpeg_audiobuf = SKIPBYTES(mpeg_audiobuf, sizeof(struct cuesheet)); + audiobuflen -= sizeof(struct cuesheet); + } + talkbuf_init(mpeg_audiobuf); +} + +static void audio_reset_buffer(void) +{ + size_t bufsize = audiobuflen; /* alloc buffer if it's was never allocated or freed by audio_hard_stop() */ if (!audiobuf_handle) - { - size_t bufsize; /* dont break strict-aliasing */ audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); - mpeg_audiobuf = core_get_data(audiobuf_handle); - audiobuflen = bufsize; - } - talkbuf_init(mpeg_audiobuf); + + audio_reset_buffer_noalloc(core_get_data(audiobuf_handle), bufsize); } void audio_play(long offset) @@ -2924,13 +2991,6 @@ static void mpeg_thread(void) void audio_init(void) { mpeg_errno = 0; - /* cuesheet support */ - if (global_settings.cuesheet) - { - int handle = core_alloc("cuesheet", sizeof(struct cuesheet)); - if (handle > 0) - curr_cuesheet = core_get_data(handle); - } talk_init(); audio_reset_buffer(); @@ -2938,10 +2998,10 @@ void audio_init(void) #ifndef SIMULATOR queue_init(&mpeg_queue, true); #endif /* !SIMULATOR */ - create_thread(mpeg_thread, mpeg_stack, - sizeof(mpeg_stack), 0, mpeg_thread_name - IF_PRIO(, PRIORITY_SYSTEM) - IF_COP(, CPU)); + audio_thread_id = create_thread(mpeg_thread, mpeg_stack, + sizeof(mpeg_stack), 0, mpeg_thread_name + IF_PRIO(, PRIORITY_SYSTEM) + IF_COP(, CPU)); memset(trackdata, 0, sizeof(trackdata)); diff --git a/apps/playback.c b/apps/playback.c index 3cc835c57..9a913871a 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -733,8 +733,6 @@ static void scratch_mem_init(void *mem) } } -/* Buffer must not move. And not shrink for now */ -static struct buflib_callbacks ops = { NULL, NULL }; static int audiobuf_handle; static size_t filebuflen; @@ -745,8 +743,9 @@ size_t audio_buffer_available(void) return core_available(); } -/* Set up the audio buffer for playback */ -static void audio_reset_buffer(void) +/* Set up the audio buffer for playback + * filebuflen must be pre-initialized with the maximum size */ +static void audio_reset_buffer_noalloc(void* filebuf) { /* * Layout audio buffer as follows: @@ -762,13 +761,6 @@ static void audio_reset_buffer(void) /* Initially set up file buffer as all space available */ size_t allocsize; - if (audiobuf_handle > 0) - { - core_free(audiobuf_handle); - audiobuf_handle = 0; - } - audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops); - unsigned char *filebuf = core_get_data(audiobuf_handle); /* Subtract whatever voice needs */ allocsize = talkbuf_init(filebuf); @@ -837,6 +829,74 @@ bufpanic: panicf("%s(): EOM (%zu > %zu)", __func__, allocsize, filebuflen); } + +/* Buffer must not move. */ +static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size) +{ + DEBUGF("%s()\n", __func__); + + long offset = audio_current_track()->offset; + int status = audio_status(); + /* TODO: Do it without stopping playback, if possible */ + /* don't call audio_hard_stop() as it frees this handle */ + if (thread_self() == audio_thread_id) + { /* inline case Q_AUDIO_STOP (audio_hard_stop() response + * if we're in the audio thread */ + audio_stop_playback(); + queue_clear(&audio_queue); + } + else + audio_queue_send(Q_AUDIO_STOP, 1); +#ifdef PLAYBACK_VOICE + voice_stop(); +#endif + /* we should be free to change the buffer now */ + size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK); + ssize_t size = (ssize_t)old_size - wanted_size; + /* set final buffer size before calling audio_reset_buffer_noalloc() */ + filebuflen = size; + DEBUGF("%s(): %zu %d\n", __func__, wanted_size, (hints & BUFLIB_SHRINK_POS_MASK) >> 30); + switch (hints & BUFLIB_SHRINK_POS_MASK) + { + case BUFLIB_SHRINK_POS_BACK: + core_shrink(handle, start, size); + audio_reset_buffer_noalloc(start); + break; + case BUFLIB_SHRINK_POS_FRONT: + core_shrink(handle, start + wanted_size, size); + audio_reset_buffer_noalloc(start + wanted_size); + break; + } + if (!(status & AUDIO_STATUS_PAUSE)) + { + DEBUGF("%s(): Resuming from %ld\n", __func__, offset); + if (thread_self() == audio_thread_id) + audio_start_playback(offset, 0); /* inline Q_AUDIO_PLAY */ + else + audio_play(offset); + } + + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = NULL, + .shrink_callback = shrink_callback, +}; + +static void audio_reset_buffer(void) +{ + if (audiobuf_handle > 0) + { + core_free(audiobuf_handle); + audiobuf_handle = 0; + } + audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops); + unsigned char *filebuf = core_get_data(audiobuf_handle); + + audio_reset_buffer_noalloc(filebuf); +} + /* Set the buffer margin to begin rebuffering when 'seconds' from empty */ static void audio_update_filebuf_watermark(int seconds) { diff --git a/apps/playlist.c b/apps/playlist.c index 58c6a9286..5e30d953e 100644 --- a/apps/playlist.c +++ b/apps/playlist.c @@ -990,14 +990,14 @@ static int sort_playlist(struct playlist_info* playlist, bool start_current, unsigned int current = playlist->indices[playlist->index]; if (playlist->amount > 0) - qsort(playlist->indices, playlist->amount, + qsort((void*)playlist->indices, playlist->amount, sizeof(playlist->indices[0]), compare); #ifdef HAVE_DIRCACHE /** We need to re-check the song names from disk because qsort can't * sort two arrays at once :/ * FIXME: Please implement a better way to do this. */ - memset(playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int)); + memset((void*)playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int)); queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0); #endif @@ -1375,7 +1375,7 @@ static int get_filename(struct playlist_info* playlist, int index, int seek, if (playlist->in_ram && !control_file && max < 0) { - max = strlcpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf)); + max = strlcpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf)); } else if (max < 0) { @@ -1531,9 +1531,10 @@ static int get_next_dir(char *dir, bool is_forward, bool recursion) break; } - files = tc->cache.entries; + files = tree_get_entries(tc); num_files = tc->filesindir; + tree_lock_cache(tc); for (i=0; icache.entries; + files = tree_get_entries(tc); num_files = tc->filesindir; for (i=0; iindices) + playlist->indices = new; + else if (current == playlist->filenames) + playlist->filenames = new; + /* buffer can possibly point to a new buffer temporarily (playlist_save()). + * just don't overwrite the pointer to that temp buffer */ + else if (current == playlist->buffer) + playlist->buffer = new; + + return BUFLIB_CB_OK; +} + + +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; +/* * Initialize playlist entries at startup */ void playlist_init(void) @@ -1938,20 +1967,23 @@ void playlist_init(void) playlist->fd = -1; playlist->control_fd = -1; playlist->max_playlist_size = global_settings.max_files_in_playlist; - handle = core_alloc("playlist idx", playlist->max_playlist_size * sizeof(int)); + handle = core_alloc_ex("playlist idx", + playlist->max_playlist_size * sizeof(int), &ops); playlist->indices = core_get_data(handle); playlist->buffer_size = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; - handle = core_alloc("playlist buf", playlist->buffer_size); + handle = core_alloc_ex("playlist buf", + playlist->buffer_size, &ops); playlist->buffer = core_get_data(handle); playlist->control_mutex = ¤t_playlist_mutex; empty_playlist(playlist, true); #ifdef HAVE_DIRCACHE - handle = core_alloc("playlist dc", playlist->max_playlist_size * sizeof(int)); + handle = core_alloc_ex("playlist dc", + playlist->max_playlist_size * sizeof(int), &ops); playlist->filenames = core_get_data(handle); - memset(playlist->filenames, 0xff, + memset((void*)playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int)); create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack), 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND) @@ -2401,7 +2433,7 @@ int playlist_add(const char *filename) #endif playlist->amount++; - strcpy(&playlist->buffer[playlist->buffer_end_pos], filename); + strcpy((char*)&playlist->buffer[playlist->buffer_end_pos], filename); playlist->buffer_end_pos += len; playlist->buffer[playlist->buffer_end_pos++] = '\0'; @@ -2728,6 +2760,7 @@ int playlist_create_ex(struct playlist_info* playlist, } playlist->buffer_size = 0; + playlist->buffer_handle = -1; playlist->buffer = NULL; playlist->control_mutex = &created_playlist_mutex; } @@ -2776,10 +2809,10 @@ int playlist_set_current(struct playlist_info* playlist) if (playlist->indices && playlist->indices != current_playlist.indices) { - memcpy(current_playlist.indices, playlist->indices, + memcpy((void*)current_playlist.indices, (void*)playlist->indices, playlist->max_playlist_size*sizeof(int)); #ifdef HAVE_DIRCACHE - memcpy(current_playlist.filenames, playlist->filenames, + memcpy((void*)current_playlist.filenames, (void*)playlist->filenames, playlist->max_playlist_size*sizeof(int)); #endif } @@ -3355,6 +3388,7 @@ int playlist_save(struct playlist_info* playlist, char *filename) char tmp_buf[MAX_PATH+1]; int result = 0; bool overwrite_current = false; + int old_handle = -1; char* old_buffer = NULL; size_t old_buffer_size = 0; @@ -3377,15 +3411,16 @@ int playlist_save(struct playlist_info* playlist, char *filename) { /* not enough buffer space to store updated indices */ /* Try to get a buffer */ - old_buffer = playlist->buffer; + old_handle = playlist->buffer_handle; + /* can ignore volatile here, because core_get_data() is called later */ + old_buffer = (char*)playlist->buffer; old_buffer_size = playlist->buffer_size; playlist->buffer = plugin_get_buffer((size_t*)&playlist->buffer_size); if (playlist->buffer_size < (int)(playlist->amount * sizeof(int))) { - playlist->buffer = old_buffer; - playlist->buffer_size = old_buffer_size; splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); - return -1; + result = -1; + goto reset_old_buffer; } } @@ -3406,12 +3441,8 @@ int playlist_save(struct playlist_info* playlist, char *filename) if (fd < 0) { splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); - if (old_buffer != NULL) - { - playlist->buffer = old_buffer; - playlist->buffer_size = old_buffer_size; - } - return -1; + result = -1; + goto reset_old_buffer; } display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false); @@ -3511,11 +3542,12 @@ int playlist_save(struct playlist_info* playlist, char *filename) } cpu_boost(false); - if (old_buffer != NULL) - { - playlist->buffer = old_buffer; - playlist->buffer_size = old_buffer_size; - } + +reset_old_buffer: + if (old_handle > 0) + old_buffer = core_get_data(old_handle); + playlist->buffer = old_buffer; + playlist->buffer_size = old_buffer_size; return result; } @@ -3531,9 +3563,9 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse, char buf[MAX_PATH+1]; int result = 0; int num_files = 0; - int i; - struct entry *files; + int i;; struct tree_context* tc = tree_get_context(); + struct tree_cache* cache = &tc->cache; int old_dirfilter = *(tc->dirfilter); if (!callback) @@ -3549,7 +3581,6 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse, return -1; } - files = tc->cache.entries; num_files = tc->filesindir; /* we've overwritten the dircache so tree browser will need to be @@ -3565,6 +3596,7 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse, break; } + struct entry *files = core_get_data(cache->entries_handle); if (files[i].attr & ATTR_DIRECTORY) { if (recurse) @@ -3583,8 +3615,7 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse, result = -1; break; } - - files = tc->cache.entries; + num_files = tc->filesindir; if (!num_files) { diff --git a/apps/playlist.h b/apps/playlist.h index f14b5c646..6dd5535df 100644 --- a/apps/playlist.h +++ b/apps/playlist.h @@ -80,15 +80,16 @@ struct playlist_info int control_fd; /* descriptor of the open control file */ bool control_created; /* has control file been created? */ int dirlen; /* Length of the path to the playlist file */ - unsigned long *indices; /* array of indices */ - int *filenames; /* Array of dircache indices */ + volatile unsigned long *indices; /* array of indices */ + volatile int *filenames; /* Array of dircache indices */ int max_playlist_size; /* Max number of files in playlist. Mirror of global_settings.max_files_in_playlist */ bool in_ram; /* playlist stored in ram (dirplay) */ + int buffer_handle; /* handle to the below buffer (-1 if non-buflib) */ union { - char *buffer; /* buffer for in-ram playlists */ - int *seek_buf; /* buffer for seeks in real playlists */ + volatile char *buffer;/* buffer for in-ram playlists */ + int *seek_buf; /* buffer for seeks in real playlists */ }; int buffer_size; /* size of buffer */ int buffer_end_pos; /* last position where buffer was written */ diff --git a/apps/plugin.c b/apps/plugin.c index 32b77ad28..43d9e03ac 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -789,6 +789,8 @@ static const struct plugin_api rockbox_api = { /* new stuff at the end, sort into place next time the API gets incompatible */ + tree_get_entries, + tree_get_entry_at, }; int plugin_load(const char* plugin, const void* parameter) @@ -865,6 +867,9 @@ int plugin_load(const char* plugin, const void* parameter) lcd_remote_update(); #endif push_current_activity(ACTIVITY_PLUGIN); + /* some plugins assume the entry cache doesn't move and save pointers to it + * they should be fixed properly instead of this lock */ + tree_lock_cache(tree_get_context()); FOR_NB_SCREENS(i) viewportmanager_theme_enable(i, false, NULL); @@ -879,6 +884,7 @@ int plugin_load(const char* plugin, const void* parameter) rc = p_hdr->entry_point(parameter); + tree_unlock_cache(tree_get_context()); pop_current_activity(); if (!pfn_tsr_exit) diff --git a/apps/plugin.h b/apps/plugin.h index d70e5634f..1d8413f6d 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -926,6 +926,8 @@ struct plugin_api { /* new stuff at the end, sort into place next time the API gets incompatible */ + struct entry* (*tree_get_entries)(struct tree_context* t); + struct entry* (*tree_get_entry_at)(struct tree_context* t, int index); }; /* plugin header */ diff --git a/apps/plugins/imageviewer/imageviewer.c b/apps/plugins/imageviewer/imageviewer.c index 80e1ba41b..044c835d0 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; static void get_pic_list(void) { struct tree_context *tree = rb->tree_get_context(); - struct entry *dircache = tree->cache.entries; + struct entry *dircache = rb->tree_get_entries(tree); int i; char *pname; diff --git a/apps/plugins/mikmod/mikmod.c b/apps/plugins/mikmod/mikmod.c index dff0fce68..d132f8049 100644 --- a/apps/plugins/mikmod/mikmod.c +++ b/apps/plugins/mikmod/mikmod.c @@ -185,7 +185,7 @@ bool mod_ext(const char ext[]) void get_mod_list(void) { struct tree_context *tree = rb->tree_get_context(); - struct entry *dircache = tree->cache.entries; + struct entry *dircache = rb->tree_get_entries(tree); int i; char *pname; diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c index 156ec019c..84eae42a7 100644 --- a/apps/plugins/mpegplayer/mpegplayer.c +++ b/apps/plugins/mpegplayer/mpegplayer.c @@ -1876,7 +1876,7 @@ static bool is_videofile(const char* file) static bool get_videofile(int direction, char* videofile, size_t bufsize) { struct tree_context *tree = rb->tree_get_context(); - struct entry *dircache = tree->cache.entries; + struct entry *dircache = rb->tree_get_entries(tree); int i, step, end, found = 0; char *videoname = rb->strrchr(videofile, '/') + 1; size_t rest = bufsize - (videoname - videofile) - 1; diff --git a/apps/plugins/rockpaint.c b/apps/plugins/rockpaint.c index add09c7fe..d1cc8f272 100644 --- a/apps/plugins/rockpaint.c +++ b/apps/plugins/rockpaint.c @@ -948,7 +948,7 @@ static bool browse_fonts( char *dst, int dst_size ) tree = rb->tree_get_context(); backup = *tree; - dc = tree->cache.entries; + dc = rb->tree_get_entries(tree); a = backup.currdir+rb->strlen(backup.currdir)-1; if( *a != '/' ) { diff --git a/apps/scrobbler.c b/apps/scrobbler.c index 3fe8e6024..78414f3d8 100644 --- a/apps/scrobbler.c +++ b/apps/scrobbler.c @@ -254,7 +254,12 @@ int scrobbler_init(void) if(!global_settings.audioscrobbler) return -1; - scrobbler_cache = core_alloc("scrobller", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); + scrobbler_cache = core_alloc("scrobbler", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); + if (scrobbler_cache <= 0) + { + logf("SCROOBLER: OOM"); + return -1; + } add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event); cache_pos = 0; @@ -288,6 +293,9 @@ void scrobbler_shutdown(void) { remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event); scrobbler_initialised = false; + /* get rid of the buffer */ + core_free(scrobbler_cache); + scrobbler_cache = 0; } } diff --git a/apps/tagcache.c b/apps/tagcache.c index 78405f7ac..af6643268 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c @@ -222,6 +222,8 @@ struct statefile_header { /* Pointer to allocated ramcache_header */ static struct ramcache_header *ramcache_hdr; +/* lock entity to temporarily prevent ramcache_hdr from moving */ +static int move_lock; #endif /** @@ -1035,6 +1037,8 @@ static bool check_clauses(struct tagcache_search *tcs, { tfe = (struct tagfile_entry *) &ramcache_hdr->tags[clause->tag][seek]; + /* str points to movable data, but no locking required here, + * as no yield() is following */ str = tfe->tag_data; } } @@ -1149,9 +1153,11 @@ static bool build_lookup_list(struct tagcache_search *tcs) # endif ) { + move_lock++; /* lock because below makes a pointer to movable data */ for (i = tcs->seek_pos; i < current_tcmh.tch.entry_count; i++) { struct tagcache_seeklist_entry *seeklist; + /* idx points to movable data, don't yield or reload */ struct index_entry *idx = &ramcache_hdr->indices[i]; if (tcs->seek_list_count == SEEK_LIST_SIZE) break ; @@ -1175,8 +1181,7 @@ static bool build_lookup_list(struct tagcache_search *tcs) /* Check for conditions. */ if (!check_clauses(tcs, idx, tcs->clause, tcs->clause_count)) continue; - - /* Add to the seek list if not already in uniq buffer. */ + /* Add to the seek list if not already in uniq buffer (doesn't yield)*/ if (!add_uniqbuf(tcs, idx->tag_seek[tcs->type])) continue; @@ -1187,6 +1192,7 @@ static bool build_lookup_list(struct tagcache_search *tcs) seeklist->idx_id = i; tcs->seek_list_count++; } + move_lock--; tcs->seek_pos = i; @@ -1538,10 +1544,11 @@ static bool get_next(struct tagcache_search *tcs) struct tagfile_entry *ep; ep = (struct tagfile_entry *)&ramcache_hdr->tags[tcs->type][tcs->position]; - tcs->result = ep->tag_data; - tcs->result_len = strlen(tcs->result) + 1; + /* don't return ep->tag_data directly as it may move */ + tcs->result_len = strlcpy(buf, ep->tag_data, sizeof(buf)) + 1; + tcs->result = buf; tcs->idx_id = ep->idx_id; - tcs->ramresult = true; + tcs->ramresult = false; /* was true before we copied to buf too */ /* Increase position for the next run. This may get overwritten. */ tcs->position += sizeof(struct tagfile_entry) + ep->tag_length; @@ -1703,15 +1710,34 @@ bool tagcache_fill_tags(struct mp3entry *id3, const char *filename) entry = &ramcache_hdr->indices[idx_id]; memset(id3, 0, sizeof(struct mp3entry)); - - id3->title = get_tag_string(entry, tag_title); - id3->artist = get_tag_string(entry, tag_artist); - id3->album = get_tag_string(entry, tag_album); - id3->genre_string = get_tag_string(entry, tag_genre); - id3->composer = get_tag_string(entry, tag_composer); - id3->comment = get_tag_string(entry, tag_comment); - id3->albumartist = get_tag_string(entry, tag_albumartist); - id3->grouping = get_tag_string(entry, tag_grouping); + char* buf = id3->id3v2buf; + ssize_t remaining = sizeof(id3->id3v2buf); + + /* this macro sets id3 strings by copying to the id3v2buf */ +#define SET(x, y) do \ + { \ + if (remaining > 0) \ + { \ + x = NULL; /* initialize with null if tag doesn't exist */ \ + char* src = get_tag_string(entry, y); \ + if (src) \ + { \ + x = buf; \ + size_t len = strlcpy(buf, src, remaining) +1; \ + buf += len; remaining -= len; \ + } \ + } \ + } while(0) + + + SET(id3->title, tag_title); + SET(id3->artist, tag_artist); + SET(id3->album, tag_album); + SET(id3->genre_string, tag_genre); + SET(id3->composer, tag_composer); + SET(id3->comment, tag_comment); + SET(id3->albumartist, tag_albumartist); + SET(id3->grouping, tag_grouping); id3->length = get_tag_numeric(entry, tag_length, idx_id); id3->playcount = get_tag_numeric(entry, tag_playcount, idx_id); @@ -2903,6 +2929,9 @@ static bool commit(void) #ifdef HAVE_DIRCACHE bool dircache_buffer_stolen = false; #endif +#ifdef HAVE_TC_RAMCACHE + bool ramcache_buffer_stolen = false; +#endif bool local_allocation = false; logf("committing tagcache"); @@ -2976,6 +3005,8 @@ static bool commit(void) tempbuf = (char *)(ramcache_hdr + 1); tempbuf_size = tc_stat.ramcache_allocated - sizeof(struct ramcache_header) - 128; tempbuf_size &= ~0x03; + move_lock++; + ramcache_buffer_stolen = true; } #endif @@ -3072,6 +3103,8 @@ static bool commit(void) #endif #ifdef HAVE_TC_RAMCACHE + if (ramcache_buffer_stolen) + move_lock--; /* Reload tagcache. */ if (tc_stat.ramcache_allocated > 0) tagcache_start_scan(); @@ -3686,9 +3719,11 @@ static bool delete_entry(long idx_id) { struct tagfile_entry *tfe; int32_t *seek = &ramcache_hdr->indices[idx_id].tag_seek[tag]; - + tfe = (struct tagfile_entry *)&ramcache_hdr->tags[tag][*seek]; + move_lock++; /* protect tfe and seek if crc_32() yield()s */ *seek = crc_32(tfe->tag_data, strlen(tfe->tag_data), 0xffffffff); + move_lock--; myidx.tag_seek[tag] = *seek; } else @@ -3810,6 +3845,30 @@ static bool check_event_queue(void) #endif #ifdef HAVE_TC_RAMCACHE + +static void fix_ramcache(void* old_addr, void* new_addr) +{ + ptrdiff_t offpos = new_addr - old_addr; + for (int i = 0; i < TAG_COUNT; i++) + ramcache_hdr->tags[i] += offpos; +} + +static int move_cb(int handle, void* current, void* new) +{ + (void)handle; + if (move_lock > 0) + return BUFLIB_CB_CANNOT_MOVE; + + fix_ramcache(current, new); + ramcache_hdr = new; + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = move_cb, + .shrink_callback = NULL, +}; + static bool allocate_tagcache(void) { struct master_header tcmh; @@ -3830,7 +3889,7 @@ static bool allocate_tagcache(void) */ tc_stat.ramcache_allocated = tcmh.tch.datasize + 256 + TAGCACHE_RESERVE + sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *); - int handle = core_alloc("tc ramcache", tc_stat.ramcache_allocated); + int handle = core_alloc_ex("tc ramcache", tc_stat.ramcache_allocated, &ops); ramcache_hdr = core_get_data(handle); memset(ramcache_hdr, 0, sizeof(struct ramcache_header)); memcpy(¤t_tcmh, &tcmh, sizeof current_tcmh); @@ -3868,12 +3927,13 @@ static bool tagcache_dumpload(void) /* Lets allocate real memory and load it */ - handle = core_alloc("tc ramcache", shdr.tc_stat.ramcache_allocated); + handle = core_alloc_ex("tc ramcache", shdr.tc_stat.ramcache_allocated, &ops); ramcache_hdr = core_get_data(handle); + moev_lock++; rc = read(fd, ramcache_hdr, shdr.tc_stat.ramcache_allocated); + move_lock--; close(fd); - - offpos = (long)ramcache_hdr - (long)shdr.hdr; + if (rc != shdr.tc_stat.ramcache_allocated) { logf("read failure!"); @@ -3884,8 +3944,7 @@ static bool tagcache_dumpload(void) memcpy(&tc_stat, &shdr.tc_stat, sizeof(struct tagcache_stat)); /* Now fix the pointers */ - for (i = 0; i < TAG_COUNT; i++) - ramcache_hdr->tags[i] += offpos; + fix_ramcache(shdr.hdr, ramcache_hdr); /* Load the tagcache master header (should match the actual DB file header). */ memcpy(¤t_tcmh, &shdr.mh, sizeof current_tcmh); @@ -3916,7 +3975,9 @@ static bool tagcache_dumpsave(void) write(fd, &shdr, sizeof shdr); /* And dump the data too */ + move_lock++; write(fd, ramcache_hdr, tc_stat.ramcache_allocated); + move_lock--; close(fd); return true; @@ -3959,7 +4020,8 @@ static bool load_tagcache(void) /* Master header copy should already match, this can be redundant to do. */ memcpy(¤t_tcmh, &tcmh, sizeof current_tcmh); - + + move_lock++; /* lock for the reset of the scan, simpler to handle */ idx = ramcache_hdr->indices; /* Load the master index table. */ @@ -3969,8 +4031,7 @@ static bool load_tagcache(void) if (bytesleft < 0) { logf("too big tagcache."); - close(fd); - return false; + goto failure; } /* DEBUG: After tagcache commit and dircache rebuild, hdr-sturcture @@ -3979,8 +4040,7 @@ static bool load_tagcache(void) if (rc != sizeof(struct index_entry)) { logf("read error #10"); - close(fd); - return false; + goto failure; } idx++; @@ -4007,7 +4067,7 @@ static bool load_tagcache(void) p += sizeof(struct tagcache_header); if ( (fd = open_tag_fd(tch, tag, false)) < 0) - return false; + goto failure_nofd; for (ramcache_hdr->entry_count[tag] = 0; ramcache_hdr->entry_count[tag] < tch->entry_count; @@ -4019,7 +4079,7 @@ static bool load_tagcache(void) { /* Abort if we got a critical event in queue */ if (check_event_queue()) - return false; + goto failure; } fe = (struct tagfile_entry *)p; @@ -4029,8 +4089,7 @@ static bool load_tagcache(void) { /* End of lookup table. */ logf("read error #11"); - close(fd); - return false; + goto failure; } /* We have a special handling for the filename tags. */ @@ -4048,16 +4107,14 @@ static bool load_tagcache(void) buf[10] = '\0'; logf("TAG:%s", buf); logf("too long filename"); - close(fd); - return false; + goto failure; } rc = read(fd, buf, fe->tag_length); if (rc != fe->tag_length) { logf("read error #12"); - close(fd); - return false; + goto failure; } /* Check if the entry has already been removed */ @@ -4068,15 +4125,13 @@ static bool load_tagcache(void) if (idx->flag & FLAG_DIRCACHE) { logf("internal error!"); - close(fd); - return false; + goto failure; } if (idx->tag_seek[tag] != pos) { logf("corrupt data structures!"); - close(fd); - return false; + goto failure; } # ifdef HAVE_DIRCACHE @@ -4123,8 +4178,7 @@ static bool load_tagcache(void) logf("too big tagcache #2"); logf("tl: %ld", fe->tag_length); logf("bl: %ld", bytesleft); - close(fd); - return false; + goto failure; } p = fe->tag_data; @@ -4138,8 +4192,7 @@ static bool load_tagcache(void) logf("len=0x%04lx", fe->tag_length); // 0x4000 logf("pos=0x%04lx", lseek(fd, 0, SEEK_CUR)); // 0x433 logf("tag=0x%02x", tag); // 0x00 - close(fd); - return false; + goto failure; } } close(fd); @@ -4148,7 +4201,14 @@ static bool load_tagcache(void) tc_stat.ramcache_used = tc_stat.ramcache_allocated - bytesleft; logf("tagcache loaded into ram!"); + move_lock--; return true; + +failure: + close(fd); +failure_nofd: + move_lock--; + return false; } #endif /* HAVE_TC_RAMCACHE */ diff --git a/apps/tagcache.h b/apps/tagcache.h index 393a2905f..6c13efdd0 100644 --- a/apps/tagcache.h +++ b/apps/tagcache.h @@ -190,7 +190,9 @@ struct tagcache_search { /* Exported variables. */ bool ramsearch; /* Is ram copy of the tagcache being used. */ - bool ramresult; /* False if result is not static, and must be copied. */ + bool ramresult; /* False if result is not static, and must be copied. + Currently always false since ramresult buffer is + movable */ int type; /* The tag type to be searched. Only nonvirtual tags */ char *result; /* The result data for all tags. */ int result_len; /* Length of the result including \0 */ diff --git a/apps/tagtree.c b/apps/tagtree.c index 0d4330bac..5766d2892 100644 --- a/apps/tagtree.c +++ b/apps/tagtree.c @@ -53,6 +53,7 @@ #include "storage.h" #include "dir.h" #include "playback.h" +#include "panic.h" #define str_or_empty(x) (x ? x : "(NULL)") @@ -60,6 +61,17 @@ static int tagtree_play_folder(struct tree_context* c); +/* this needs to be same size as struct entry (tree.h) and name needs to be + * the first; so that they're compatible enough to walk arrays of both + * derefencing the name member*/ +struct tagentry { + char* name; + int newtable; + int extraseek; +}; + +static struct tagentry* tagtree_get_entry(struct tree_context *c, int id); + #define SEARCHSTR_SIZE 256 enum table { @@ -96,7 +108,7 @@ enum variables { /* Capacity 10 000 entries (for example 10k different artists) */ #define UNIQBUF_SIZE (64*1024) -static long *uniqbuf; +static long uniqbuf[UNIQBUF_SIZE / sizeof(long)]; #define MAX_TAGS 5 #define MAX_MENU_ID_SIZE 32 @@ -163,8 +175,8 @@ struct match /* Statusbar text of the current view. */ static char current_title[MAX_TAGS][128]; -static struct menu_root *menus[TAGMENU_MAX_MENUS]; -static struct menu_root *menu; +static struct menu_root * menus[TAGMENU_MAX_MENUS]; +static struct menu_root * menu; static struct search_instruction *csi; static const char *strp; static int menu_count; @@ -176,8 +188,74 @@ static int current_entry_count; static struct tree_context *tc; /* a few memory alloc helper */ -static int tagtree_handle; +static int tagtree_handle, lock_count; static size_t tagtree_bufsize, tagtree_buf_used; + +#define UPDATE(x, y) { x = (typeof(x))((char*)(x) + (y)); } +static int move_callback(int handle, void* current, void* new) +{ + (void)handle; (void)current; (void)new; + ptrdiff_t diff = new - current; + + if (lock_count > 0) + return BUFLIB_CB_CANNOT_MOVE; + + UPDATE(menu, diff); + /* loop over menus */ + for(int i = 0; i < menu_count; i++) + { + struct menu_root* menu = menus[i]; + /* then over the menu_entries of a menu */ + for(int j = 0; j < menu->itemcount; j++) + { + struct menu_entry* mentry = menu->items[j]; + /* then over the search_instructions of each menu_entry */ + for(int k = 0; k < mentry->si.tagorder_count; k++) + { + for(int l = 0; l < mentry->si.clause_count[k]; l++) + { + UPDATE(mentry->si.clause[k][l]->str, diff); + UPDATE(mentry->si.clause[k][l], diff); + } + } + UPDATE(menu->items[j], diff); + } + UPDATE(menus[i], diff); + } + + /* now the same game for formats */ + for(int i = 0; i < format_count; i++) + { + for(int j = 0; j < formats[i]->clause_count; j++) + { + UPDATE(formats[i]->clause[j]->str, diff); + UPDATE(formats[i]->clause[j], diff); + } + + if (formats[i]->formatstr) + UPDATE(formats[i]->formatstr, diff); + + UPDATE(formats[i], diff); + } + return BUFLIB_CB_OK; +} +#undef UPDATE + +static inline void tagtree_lock(void) +{ + lock_count++; +} + +static inline void tagtree_unlock(void) +{ + lock_count--; +} + +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; + static void* tagtree_alloc(size_t size) { char* buf = core_get_data(tagtree_handle) + tagtree_buf_used; @@ -201,6 +279,7 @@ static char* tagtree_strdup(const char* buf) return dest; } +/* save to call without locking */ static int get_token_str(char *buf, int size) { /* Find the start. */ @@ -510,7 +589,8 @@ static int add_format(const char *buf) { int clause_count = 0; strp++; - + + tagtree_lock(); while (1) { struct tagcache_search_clause *newclause; @@ -529,6 +609,7 @@ static int add_format(const char *buf) clause_count++; } + tagtree_unlock(); formats[format_count]->clause_count = clause_count; } @@ -593,9 +674,14 @@ static int get_condition(struct search_instruction *inst) strp++; new_clause->type = clause_logical_or; } - else if (!read_clause(new_clause)) - return -1; - + else + { + tagtree_lock(); + bool ret = read_clause(new_clause); + tagtree_unlock(); + if (!ret) + return -1; + } inst->clause_count[inst->tagorder_count]++; return 1; @@ -616,7 +702,6 @@ static bool parse_search(struct menu_entry *entry, const char *str) struct search_instruction *inst = &entry->si; char buf[MAX_PATH]; int i; - struct menu_root *new_menu; strp = str; @@ -654,8 +739,7 @@ static bool parse_search(struct menu_entry *entry, const char *str) /* Allocate a new menu unless link is found. */ menus[menu_count] = tagtree_alloc0(sizeof(struct menu_root)); - new_menu = menus[menu_count]; - strlcpy(new_menu->id, buf, MAX_MENU_ID_SIZE); + strlcpy(menus[menu_count]->id, buf, MAX_MENU_ID_SIZE); entry->link = menu_count; ++menu_count; @@ -679,8 +763,11 @@ static bool parse_search(struct menu_entry *entry, const char *str) break ; logf("tag: %d", inst->tagorder[inst->tagorder_count]); - + + tagtree_lock(); while ( (ret = get_condition(inst)) > 0 ) ; + tagtree_unlock(); + if (ret < 0) return false; @@ -697,7 +784,7 @@ static int compare(const void *p1, const void *p2) { struct tagentry *e1 = (struct tagentry *)p1; struct tagentry *e2 = (struct tagentry *)p2; - + if (sort_inverse) return strncasecmp(e2->name, e1->name, MAX_PATH); @@ -1001,11 +1088,11 @@ static int parse_line(int n, char *buf, void *parameters) if (menu->items[menu->itemcount] == NULL) menu->items[menu->itemcount] = tagtree_alloc0(sizeof(struct menu_entry)); - if (!parse_search(menu->items[menu->itemcount], buf)) - return 0; - - menu->itemcount++; - + tagtree_lock(); + if (parse_search(menu->items[menu->itemcount], buf)) + menu->itemcount++; + tagtree_unlock(); + return 0; } @@ -1040,15 +1127,20 @@ void tagtree_init(void) menu_count = 0; menu = NULL; rootmenu = -1; - tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, NULL); + tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, &ops); parse_menu(FILE_SEARCH_INSTRUCTIONS); + + /* safety check since tree.c needs to cast tagentry to entry */ + if (sizeof(struct tagentry) != sizeof(struct entry)) + panicf("tagentry(%zu) and entry mismatch(%zu)", + sizeof(struct tagentry), sizeof(struct entry)); + if (lock_count > 0) + panicf("tagtree locked after parsing"); /* If no root menu is set, assume it's the first single menu * we have. That shouldn't normally happen. */ if (rootmenu < 0) rootmenu = 0; - - uniqbuf = tagtree_alloc(UNIQBUF_SIZE); add_event(PLAYBACK_EVENT_TRACK_BUFFER, false, tagtree_buffer_event); 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, return 0; } +static struct tagentry* get_entries(struct tree_context *tc) +{ + return core_get_data(tc->cache.entries_handle); +} + static int retrieve_entries(struct tree_context *c, int offset, bool init) { struct tagcache_search tcs; - struct tagentry *dptr = c->cache.entries; struct display_format *fmt; int i; int namebufused = 0; @@ -1242,7 +1338,10 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) csi->result_seek[i]); } } - + + /* because tagcache saves the clauses, we need to lock the buffer + * for the entire duration of the search */ + tagtree_lock(); for (i = 0; i <= level; i++) { int j; @@ -1276,6 +1375,10 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) strip = 0; } + /* lock buflib out due to possible yields */ + tree_lock_cache(c); + struct tagentry *dptr = core_get_data(c->cache.entries_handle); + if (tag != tag_title && tag != tag_filename) { if (offset == 0) @@ -1315,6 +1418,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) fmt = NULL; /* Check the format */ + tagtree_lock(); for (i = 0; i < format_count; i++) { 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) break; } } + tagtree_unlock(); if (strcmp(tcs.result, UNTAGGED) == 0) { @@ -1337,7 +1442,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) if (!tcs.ramresult || fmt) { - dptr->name = &c->cache.name_buffer[namebufused]; + dptr->name = core_get_data(c->cache.name_buffer_handle)+namebufused; if (fmt) { @@ -1354,6 +1459,8 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) { logf("format_str() failed"); tagcache_search_finish(&tcs); + tree_unlock_cache(c); + tagtree_unlock(); return 0; } else @@ -1392,6 +1499,8 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) if (!show_search_progress(false, total_count)) { /* user aborted */ tagcache_search_finish(&tcs); + tree_unlock_cache(c); + tagtree_unlock(); return current_entry_count; } } @@ -1399,15 +1508,17 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) if (sort) { - int entry_size = sizeof(struct tagentry); - qsort(c->cache.entries + special_entry_count * entry_size, + struct tagentry *entries = get_entries(c); + qsort(&entries[special_entry_count], current_entry_count - special_entry_count, - entry_size, compare); + sizeof(struct tagentry), compare); } if (!init) { tagcache_search_finish(&tcs); + tree_unlock_cache(c); + tagtree_unlock(); return current_entry_count; } @@ -1422,7 +1533,9 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) } tagcache_search_finish(&tcs); - + tree_unlock_cache(c); + tagtree_unlock(); + if (!sort && (sort_inverse || sort_limit)) { 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) if (strip) { - dptr = c->cache.entries; + dptr = get_entries(c); for (i = special_entry_count; i < current_entry_count; i++, dptr++) { int len = strlen(dptr->name); @@ -1446,14 +1559,14 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) dptr->name = &dptr->name[strip]; } } - + return total_count; } static int load_root(struct tree_context *c) { - struct tagentry *dptr = c->cache.entries; + struct tagentry *dptr = core_get_data(c->cache.entries_handle); int i; tc = c; @@ -1569,6 +1682,10 @@ int tagtree_enter(struct tree_context* c) c->pos_history[c->dirlevel] = c->firstpos; c->dirlevel++; + /* lock buflib for possible I/O to protect dptr */ + tree_lock_cache(c); + tagtree_lock(); + switch (c->currtable) { case ROOT: c->currextra = newextra; @@ -1634,6 +1751,8 @@ int tagtree_enter(struct tree_context* c) if (rc < 0 || !searchstring[0]) { tagtree_exit(c); + tree_unlock_cache(c); + tagtree_unlock(); return 0; } if (csi->clause[i][j]->numeric) @@ -1682,9 +1801,12 @@ int tagtree_enter(struct tree_context* c) c->dirlevel--; break; } + c->selected_item=0; gui_synclist_select_item(&tree_lists, c->selected_item); + tree_unlock_cache(c); + tagtree_unlock(); return rc; } @@ -1704,14 +1826,13 @@ void tagtree_exit(struct tree_context* c) int tagtree_get_filename(struct tree_context* c, char *buf, int buflen) { struct tagcache_search tcs; - struct tagentry *entry; + int extraseek = tagtree_get_entry(c, c->selected_item)->extraseek; - entry = tagtree_get_entry(c, c->selected_item); if (!tagcache_search(&tcs, tag_filename)) return -1; - if (!tagcache_retrieve(&tcs, entry->extraseek, tcs.type, buf, buflen)) + if (!tagcache_retrieve(&tcs, extraseek, tcs.type, buf, buflen)) { tagcache_search_finish(&tcs); return -2; @@ -1790,9 +1911,9 @@ static bool insert_all_playlist(struct tree_context *c, int position, bool queue bool tagtree_insert_selection_playlist(int position, bool queue) { - struct tagentry *dptr; char buf[MAX_PATH]; int dirlevel = tc->dirlevel; + int newtable; show_search_progress( #ifdef HAVE_DISK_STORAGE @@ -1804,10 +1925,10 @@ bool tagtree_insert_selection_playlist(int position, bool queue) /* We need to set the table to allsubentries. */ - dptr = tagtree_get_entry(tc, tc->selected_item); + newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; /* Insert a single track? */ - if (dptr->newtable == PLAYTRACK) + if (newtable == PLAYTRACK) { if (tagtree_get_filename(tc, buf, sizeof buf) < 0) { @@ -1819,29 +1940,29 @@ bool tagtree_insert_selection_playlist(int position, bool queue) return true; } - if (dptr->newtable == NAVIBROWSE) + if (newtable == NAVIBROWSE) { tagtree_enter(tc); tagtree_load(tc); - dptr = tagtree_get_entry(tc, tc->selected_item); + newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; } - else if (dptr->newtable != ALLSUBENTRIES) + else if (newtable != ALLSUBENTRIES) { - logf("unsupported table: %d", dptr->newtable); + logf("unsupported table: %d", newtable); return false; } /* Now the current table should be allsubentries. */ - if (dptr->newtable != PLAYTRACK) + if (newtable != PLAYTRACK) { tagtree_enter(tc); tagtree_load(tc); - dptr = tagtree_get_entry(tc, tc->selected_item); + newtable = tagtree_get_entry(tc, tc->selected_item)->newtable; /* And now the newtable should be playtrack. */ - if (dptr->newtable != PLAYTRACK) + if (newtable != PLAYTRACK) { - logf("newtable: %d !!", dptr->newtable); + logf("newtable: %d !!", newtable); tc->dirlevel = dirlevel; return false; } @@ -1886,9 +2007,9 @@ static int tagtree_play_folder(struct tree_context* c) return 0; } -struct tagentry* tagtree_get_entry(struct tree_context *c, int id) +static struct tagentry* tagtree_get_entry(struct tree_context *c, int id) { - struct tagentry *entry = (struct tagentry *)c->cache.entries; + struct tagentry *entry; int realid = id - current_offset; /* Load the next chunk if necessary. */ @@ -1905,10 +2026,22 @@ struct tagentry* tagtree_get_entry(struct tree_context *c, int id) realid = id - current_offset; cpu_boost(false); } - + + entry = get_entries(c); return &entry[realid]; } +char* tagtree_get_entry_name(struct tree_context *c, int id, + char* buf, size_t bufsize) +{ + struct tagentry *entry = tagtree_get_entry(c, id); + if (!entry) + return NULL; + strlcpy(buf, entry->name, bufsize); + return buf; +} + + char *tagtree_get_title(struct tree_context* c) { switch (c->currtable) diff --git a/apps/tagtree.h b/apps/tagtree.h index aaf5158e5..26952b40b 100644 --- a/apps/tagtree.h +++ b/apps/tagtree.h @@ -30,19 +30,14 @@ #define TAGMENU_MAX_MENUS 32 #define TAGMENU_MAX_FMTS 32 -struct tagentry { - char *name; - int newtable; - int extraseek; -}; - bool tagtree_export(void); bool tagtree_import(void); void tagtree_init(void) INIT_ATTR; int tagtree_enter(struct tree_context* c); void tagtree_exit(struct tree_context* c); int tagtree_load(struct tree_context* c); -struct tagentry* tagtree_get_entry(struct tree_context *c, int id); +char* tagtree_get_entry_name(struct tree_context *c, int id, + char* buf, size_t bufsize); bool tagtree_insert_selection_playlist(int position, bool queue); char *tagtree_get_title(struct tree_context* c); int tagtree_get_attr(struct tree_context* c); diff --git a/apps/talk.c b/apps/talk.c index 811a1ad70..6fc52f4e8 100644 --- a/apps/talk.c +++ b/apps/talk.c @@ -131,6 +131,8 @@ static uint8_t clip_age[QUEUE_SIZE]; static char* voicebuf; /* root pointer to our buffer */ static unsigned char* p_thumbnail = NULL; /* buffer for thumbnails */ + /* protects p_thumbnail during during I/O and queue_clip() */ +static int thumbnail_buf_locked; /* Multiple thumbnails can be loaded back-to-back in this buffer. */ static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in thumbnail buffer */ @@ -601,7 +603,23 @@ static void queue_clip(unsigned char* buf, long size, bool enqueue) return; } - +#if CONFIG_CODEC == SWCODEC +static int move_callback(int handle, void* current, void* new) +{ + (void)handle;(void)current; + if (thumbnail_buf_locked > 0) + return BUFLIB_CB_CANNOT_MOVE; + /* shut up in case a thumbnail is playling right now */ + if (thumbnail_buf_used > 0) + talk_force_shutup(); + p_thumbnail = new; + return BUFLIB_CB_OK; +} +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; +#endif static void alloc_thumbnail_buf(void) { #if CONFIG_CODEC == SWCODEC @@ -611,7 +629,7 @@ static void alloc_thumbnail_buf(void) size_for_thumbnail = core_available(); if (size_for_thumbnail > MAX_THUMBNAIL_BUFSIZE) size_for_thumbnail = MAX_THUMBNAIL_BUFSIZE; - int handle = core_alloc("thumbnail", size_for_thumbnail); + int handle = core_alloc_ex("thumbnail", size_for_thumbnail, &ops); p_thumbnail = core_get_data(handle); } #else @@ -892,8 +910,10 @@ static int _talk_file(const char* filename, lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */ #endif + thumbnail_buf_locked++; size = read(fd, p_thumbnail +thumb_used, size_for_thumbnail -thumb_used); + thumbnail_buf_locked--; close(fd); /* ToDo: find audio, skip ID headers and trailers */ @@ -911,7 +931,9 @@ static int _talk_file(const char* filename, talk_queue_lock(); thumbnail_buf_used = thumb_used +size; talk_queue_unlock(); + thumbnail_buf_locked++; queue_clip(p_thumbnail +thumb_used, size, true); + thumbnail_buf_locked--; } return size; diff --git a/apps/tdspeed.c b/apps/tdspeed.c index 2000952e8..f1fac20d4 100644 --- a/apps/tdspeed.c +++ b/apps/tdspeed.c @@ -38,9 +38,45 @@ #define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */ +static int32_t** dsp_src; +static int handles[4]; static int32_t *overlap_buffer[2] = { NULL, NULL }; static int32_t *outbuf[2] = { NULL, NULL }; +static int move_callback(int handle, void* current, void* new) +{ + /* TODO */ + (void)handle; + if (dsp_src) + { + int ch = (current == outbuf[0]) ? 0 : 1; + dsp_src[ch] = outbuf[ch] = new; + } + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; +static int ovl_move_callback(int handle, void* current, void* new) +{ + /* TODO */ + (void)handle; + if (dsp_src) + { + int ch = (current == overlap_buffer[0]) ? 0 : 1; + overlap_buffer[ch] = new; + } + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ovl_ops = { + .move_callback = ovl_move_callback, + .shrink_callback = NULL, +}; + + struct tdspeed_state_s { bool stereo; @@ -60,30 +96,42 @@ void tdspeed_init() if (global_settings.timestretch_enabled) { /* Allocate buffers */ - int handle; if (overlap_buffer[0] == NULL) { - handle = core_alloc("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t)); - overlap_buffer[0] = core_get_data(handle); + handles[0] = core_alloc_ex("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops); + overlap_buffer[0] = core_get_data(handles[0]); } if (overlap_buffer[1] == NULL) { - handle = core_alloc("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t)); - overlap_buffer[1] = core_get_data(handle); + handles[1] = core_alloc_ex("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops); + overlap_buffer[1] = core_get_data(handles[1]); } if (outbuf[0] == NULL) { - handle = core_alloc("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t)); - outbuf[0] = core_get_data(handle); + handles[2] = core_alloc_ex("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops); + outbuf[0] = core_get_data(handles[2]); } if (outbuf[1] == NULL) { - handle = core_alloc("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t)); - outbuf[1] = core_get_data(handle); + handles[3] = core_alloc_ex("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops); + outbuf[1] = core_get_data(handles[3]); } } } +void tdspeed_finish(void) +{ + for(unsigned i = 0; i < ARRAYLEN(handles); i++) + { + if (handles[i] > 0) + { + core_free(handles[i]); + handles[i] = 0; + } + } + overlap_buffer[0] = overlap_buffer[1] = NULL; + outbuf[0] = outbuf[1] = NULL; +} bool tdspeed_config(int samplerate, bool stereo, int32_t factor) { @@ -338,6 +386,7 @@ long tdspeed_est_input_size(long size) int tdspeed_doit(int32_t *src[], int count) { + dsp_src = src; count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] }, src, count, 0, TDSPEED_OUTBUFSIZE); src[0] = outbuf[0]; diff --git a/apps/tdspeed.h b/apps/tdspeed.h index c3b7fc463..e91eeb170 100644 --- a/apps/tdspeed.h +++ b/apps/tdspeed.h @@ -36,7 +36,8 @@ #define GET_STRETCH(pitch, speed) \ ((speed * PITCH_SPEED_100 + pitch / 2L) / pitch) -void tdspeed_init(void) INIT_ATTR; +void tdspeed_init(void); +void tdspeed_finish(void); bool tdspeed_config(int samplerate, bool stereo, int32_t factor); long tdspeed_est_output_size(void); long tdspeed_est_input_size(long size); diff --git a/apps/tree.c b/apps/tree.c index 211ddb2f9..c7484ff42 100644 --- a/apps/tree.c +++ b/apps/tree.c @@ -104,12 +104,18 @@ static int ft_play_dirname(char* name); static void ft_play_filename(char *dir, char *file); static void say_filetype(int attr); -static struct entry* get_entry_at(struct tree_context *t, int index) +struct entry* tree_get_entries(struct tree_context *t) { - struct entry* entries = t->cache.entries; + return core_get_data(t->cache.entries_handle); +} + +struct entry* tree_get_entry_at(struct tree_context *t, int index) +{ + struct entry* entries = tree_get_entries(t); return &entries[index]; } + static const char* tree_get_filename(int selected_item, void *data, char *buffer, size_t buffer_len) { @@ -122,12 +128,12 @@ static const char* tree_get_filename(int selected_item, void *data, if (id3db) { - return tagtree_get_entry(&tc, selected_item)->name; + return tagtree_get_entry_name(&tc, selected_item, buffer, buffer_len); } else #endif { - struct entry* e = get_entry_at(local_tc, selected_item); + struct entry* e = tree_get_entry_at(local_tc, selected_item); name = e->name; attr = e->attr; } @@ -169,7 +175,7 @@ static int tree_get_filecolor(int selected_item, void * data) if (*tc.dirfilter == SHOW_ID3DB) return -1; struct tree_context * local_tc=(struct tree_context *)data; - struct entry* e = get_entry_at(local_tc, selected_item); + struct entry* e = tree_get_entry_at(local_tc, selected_item); return filetype_get_color(e->name, e->attr); } #endif @@ -185,7 +191,7 @@ static enum themable_icons tree_get_fileicon(int selected_item, void * data) else #endif { - struct entry* e = get_entry_at(local_tc, selected_item); + struct entry* e = tree_get_entry_at(local_tc, selected_item); return filetype_get_icon(e->attr); } } @@ -197,16 +203,17 @@ static int tree_voice_cb(int selected_item, void * data) int attr=0; #ifdef HAVE_TAGCACHE bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB; + char buf[AVERAGE_FILENAME_LENGTH*2]; if (id3db) { attr = tagtree_get_attr(local_tc); - name = tagtree_get_entry(local_tc, selected_item)->name; + name = tagtree_get_entry_name(local_tc, selected_item, buf, sizeof(buf)); } else #endif { - struct entry* e = get_entry_at(local_tc, selected_item); + struct entry* e = tree_get_entry_at(local_tc, selected_item); name = e->name; attr = e->attr; } @@ -329,7 +336,7 @@ static int tree_get_file_position(char * filename) /* use lastfile to determine the selected item (default=0) */ for (i=0; i < tc.filesindir; i++) { - e = get_entry_at(&tc, i); + e = tree_get_entry_at(&tc, i); if (!strcasecmp(e->name, filename)) return(i); } @@ -531,7 +538,7 @@ char* get_current_file(char* buffer, size_t buffer_len) return NULL; #endif - struct entry* e = get_entry_at(&tc, tc.selected_item); + struct entry* e = tree_get_entry_at(&tc, tc.selected_item); if (getcwd(buffer, buffer_len)) { if (tc.dirlength) @@ -650,7 +657,6 @@ static int dirbrowse(void) gui_synclist_draw(&tree_lists); while(1) { - struct entry *entries = tc.cache.entries; bool restore = false; if (tc.dirlevel < 0) tc.dirlevel = 0; /* shouldnt be needed.. this code needs work! */ @@ -666,8 +672,9 @@ static int dirbrowse(void) if ( numentries == 0 ) break; + short attr = tree_get_entry_at(&tc, tc.selected_item)->attr; if ((tc.browse->flags & BROWSE_SELECTONLY) && - !(entries[tc.selected_item].attr & ATTR_DIRECTORY)) + !(attr & ATTR_DIRECTORY)) { tc.browse->flags |= BROWSE_SELECTED; get_current_file(tc.browse->buf, tc.browse->bufsize); @@ -792,15 +799,14 @@ static int dirbrowse(void) else #endif { - attr = entries[tc.selected_item].attr; + struct entry *entry = tree_get_entry_at(&tc, tc.selected_item); + attr = entry->attr; if (currdir[1]) /* Not in / */ snprintf(buf, sizeof buf, "%s/%s", - currdir, - entries[tc.selected_item].name); + currdir, entry->name); else /* In / */ - snprintf(buf, sizeof buf, "/%s", - entries[tc.selected_item].name); + snprintf(buf, sizeof buf, "/%s", entry->name); } onplay_result = onplay(buf, attr, curr_context, hotkey); } @@ -999,10 +1005,36 @@ int rockbox_browse(struct browse_context *browse) return ret_val; } +static int move_callback(int handle, void* current, void* new) +{ + struct tree_cache* cache = &tc.cache; + if (cache->lock_count > 0) + return BUFLIB_CB_CANNOT_MOVE; + + size_t diff = new - current; + /* FIX_PTR makes sure to not accidentally update static allocations */ +#define FIX_PTR(x) \ + { if ((void*)x > current && (void*)x < (current+cache->name_buffer_size)) x+= diff; } + + if (handle == cache->name_buffer_handle) + { /* update entry structs, *even if they are struct tagentry */ + struct entry *this = core_get_data(cache->entries_handle); + struct entry *last = this + cache->max_entries; + for(; this < last; this++) + FIX_PTR(this->name); + } + /* nothing to do if entries moved */ + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; + void tree_mem_init(void) { /* initialize tree context struct */ - int handle; struct tree_cache* cache = &tc.cache; memset(&tc, 0, sizeof(tc)); tc.dirfilter = &global_settings.dirfilter; @@ -1010,12 +1042,14 @@ void tree_mem_init(void) cache->name_buffer_size = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; - handle = core_alloc("tree names", cache->name_buffer_size); - cache->name_buffer = core_get_data(handle); + cache->name_buffer_handle = core_alloc_ex("tree names", + cache->name_buffer_size, + &ops); cache->max_entries = global_settings.max_files_in_dir; - handle = core_alloc("tree entries", cache->max_entries*(sizeof(struct entry))); - cache->entries = core_get_data(handle); + cache->entries_handle = core_alloc_ex("tree entries", + cache->max_entries*(sizeof(struct entry)), + &ops); tree_get_filetypes(&filetypes, &filetypes_count); } diff --git a/apps/tree.h b/apps/tree.h index c07b92f29..2b296050d 100644 --- a/apps/tree.h +++ b/apps/tree.h @@ -26,26 +26,30 @@ #include #include "icon.h" +/* keep this struct compatible (total size and name member) + * with struct tagtree_entry (tagtree.h) */ struct entry { - short attr; /* FAT attributes + file type flags */ - unsigned long time_write; /* Last write time */ char *name; + int attr; /* FAT attributes + file type flags */ + unsigned time_write; /* Last write time */ }; - #define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */ #define BROWSE_NO_CONTEXT_MENU 0x0002 /* disable context menu */ #define BROWSE_SELECTED 0x0100 /* this bit is set if user selected item */ struct tree_context; + struct tree_cache { - /* A big buffer with plenty of entry structs, - * contains all files and dirs in the current - * dir (with filters applied) */ - void* entries; - char* name_buffer; - int max_entries; /* Max entries in the cache */ - int name_buffer_size; /* in bytes */ + /* A big buffer with plenty of entry structs, contains all files and dirs + * in the current dir (with filters applied) + * Note that they're buflib-allocated and can therefore possibly move + * They need to be locked if used around yielding functions */ + int entries_handle; /* handle to the entry cache */ + int name_buffer_handle; /* handle to the name cache */ + int max_entries; /* Max entries in the cache */ + int name_buffer_size; /* in bytes */ + volatile int lock_count; /* non-0 if buffers may not move */ }; struct browse_context { @@ -95,6 +99,10 @@ struct tree_context { struct browse_context *browse; }; +/* + * Call one of the two below after yields since the entrys may move inbetween */ +struct entry* tree_get_entries(struct tree_context *t); +struct entry* tree_get_entry_at(struct tree_context *t, int index); void tree_drawlists(void); void tree_mem_init(void) INIT_ATTR; void tree_gui_init(void) INIT_ATTR; @@ -108,6 +116,14 @@ void browse_context_init(struct browse_context *browse, int rockbox_browse(struct browse_context *browse); bool create_playlist(void); void resume_directory(const char *dir); +static inline void tree_lock_cache(struct tree_context *t) +{ + t->cache.lock_count++; +} +static inline void tree_unlock_cache(struct tree_context *t) +{ + t->cache.lock_count--; +} #ifdef WIN32 /* it takes an int on windows */ #define getcwd_size_t int diff --git a/firmware/buflib.c b/firmware/buflib.c index a0c2005f1..baeb26ba7 100644 --- a/firmware/buflib.c +++ b/firmware/buflib.c @@ -192,10 +192,6 @@ handle_table_shrink(struct buflib_context *ctx) static bool move_block(struct buflib_context* ctx, union buflib_data* block, int shift) { -#if 1 /* moving temporarily disabled */ - (void)ctx;(void)block;(void)shift; - return false; -#else char* new_start; union buflib_data *new_block, *tmp = block[1].handle; struct buflib_callbacks *ops = block[2].ops; @@ -218,7 +214,6 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift) memmove(new_block, block, block->val * sizeof(union buflib_data)); return true; -#endif } /* Compact allocations and handle table, adjusting handle pointers as needed. diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c index 9e8681e1d..152438a3e 100644 --- a/firmware/common/dircache.c +++ b/firmware/common/dircache.c @@ -66,12 +66,21 @@ struct fdbind_queue { int fd; }; -/* Exported structures. */ +/* Unions with char to make pointer arithmetic simpler and avoid casting */ struct dircache_entry { struct dirinfo info; - struct dircache_entry *next; - struct dircache_entry *up; - struct dircache_entry *down; + union { + struct dircache_entry *next; + char* next_char; + }; + union { + struct dircache_entry *up; + char* up_char; + }; + union { + struct dircache_entry *down; + char* down_char; + }; long startcluster; char *d_name; }; @@ -130,6 +139,44 @@ static inline struct dircache_entry* get_entry(int id) return &dircache_root[id]; } +/* flag to make sure buffer doesn't move due to other allocs. + * this is set to true completely during dircache build */ +static bool dont_move = false; +static int dircache_handle; +static int move_callback(int handle, void* current, void* new) +{ + (void)handle; + if (dont_move) + return BUFLIB_CB_CANNOT_MOVE; + + /* relocate the cache */ + ptrdiff_t diff = new - current; + for(unsigned i = 0; i < entry_count; i++) + { + if (dircache_root[i].d_name) + dircache_root[i].d_name += diff; + if (dircache_root[i].next_char) + dircache_root[i].next_char += diff; + if (dircache_root[i].up_char) + dircache_root[i].up_char += diff; + if (dircache_root[i].down_char) + dircache_root[i].down_char += diff; + } + dircache_root = new; + + d_names_start -= diff; + d_names_end -= diff; + dot -= diff; + dotdot -= diff; + + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; + #ifdef HAVE_EEPROM_SETTINGS /** * Open the dircache file to save a snapshot on disk @@ -573,10 +620,11 @@ int dircache_load(void) } allocated_size = maindata.size + DIRCACHE_RESERVE; - int handle = core_alloc("dircache", allocated_size); - dircache_root = core_get_data(handle); - /* needs to be struct-size aligned so that the pointer arithmetic below works */ - ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); + dircache_handle = core_alloc_ex("dircache", allocated_size, &ops); + /* block movement during upcoming I/O */ + dont_move = true; + dircache_root = core_get_data(dircache_handle); + ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry*)); entry_count = maindata.entry_count; appflags = maindata.appflags; @@ -608,8 +656,9 @@ int dircache_load(void) dotdot = dot - sizeof(".."); /* d_names are in reverse order, so the last entry points to the first string */ - ptrdiff_t offset_d_names = maindata.d_names_start - d_names_start, - offset_entries = maindata.root_entry - dircache_root; + ptrdiff_t offset_d_names = maindata.d_names_start - d_names_start; + ptrdiff_t offset_entries = maindata.root_entry - dircache_root; + offset_entries *= sizeof(struct dircache_entry); /* make it bytes */ /* offset_entries is less likely to differ, so check if it's 0 in the loop * offset_d_names however is almost always non-zero, since dircache_save() @@ -625,12 +674,12 @@ int dircache_load(void) if (offset_entries == 0) continue; - if (dircache_root[i].next) - dircache_root[i].next -= offset_entries; - if (dircache_root[i].up) - dircache_root[i].up -= offset_entries; - if (dircache_root[i].down) - dircache_root[i].down -= offset_entries; + if (dircache_root[i].next_char) + dircache_root[i].next_char -= offset_entries; + if (dircache_root[i].up_char) + dircache_root[i].up_char -= offset_entries; + if (dircache_root[i].down_char) + dircache_root[i].down_char -= offset_entries; } } @@ -640,6 +689,7 @@ int dircache_load(void) logf("Done, %ld KiB used", dircache_size / 1024); dircache_initialized = true; memset(fd_bindings, 0, sizeof(fd_bindings)); + dont_move = false; return 0; } @@ -660,6 +710,7 @@ int dircache_save(void) return -1; logf("Saving directory cache"); + dont_move = true; fd = open_dircache_file(O_WRONLY | O_CREAT | O_TRUNC, 0666); maindata.magic = DIRCACHE_MAGIC; @@ -698,7 +749,7 @@ int dircache_save(void) return -4; } - + dont_move = false; return 0; } #endif /* HAVE_EEPROM_SETTINGS */ @@ -720,6 +771,7 @@ static int dircache_do_rebuild(void) /* reset dircache and alloc root entry */ entry_count = 0; root_entry = allocate_entry(); + dont_move = true; #ifdef HAVE_MULTIVOLUME append_position = root_entry; @@ -740,6 +792,7 @@ static int dircache_do_rebuild(void) cpu_boost(false); dircache_size = 0; dircache_initializing = false; + dont_move = false; return -2; } cpu_boost(false); @@ -765,7 +818,8 @@ static int dircache_do_rebuild(void) if (allocated_size - dircache_size < DIRCACHE_RESERVE) reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size); } - + + dont_move = false; return 1; } @@ -790,7 +844,8 @@ static void dircache_thread(void) #endif case DIRCACHE_BUILD: thread_enabled = true; - dircache_do_rebuild(); + if (dircache_do_rebuild() < 0) + core_free(dircache_handle); thread_enabled = false; break ; @@ -848,11 +903,10 @@ int dircache_build(int last_size) if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT ) { - int handle; allocated_size = last_size + DIRCACHE_RESERVE; - handle = core_alloc("dircache", allocated_size); - dircache_root = core_get_data(handle); - ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); + dircache_handle = core_alloc_ex("dircache", allocated_size, &ops); + dircache_root = core_get_data(dircache_handle); + ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry*)); d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1; dircache_size = 0; thread_enabled = true; @@ -869,10 +923,10 @@ int dircache_build(int last_size) * after generation the buffer will be compacted with DIRCACHE_RESERVE * free bytes inbetween */ size_t got_size; - int handle = core_alloc_maximum("dircache", &got_size, NULL); - char* buf = core_get_data(handle); + dircache_handle = core_alloc_maximum("dircache", &got_size, &ops); + char* buf = core_get_data(dircache_handle); dircache_root = (struct dircache_entry*)ALIGN_UP(buf, - sizeof(struct dircache_entry)); + sizeof(struct dircache_entry*)); d_names_start = d_names_end = buf + got_size - 1; dircache_size = 0; generate_dot_d_names(); @@ -909,11 +963,11 @@ int dircache_build(int last_size) allocated_size = (d_names_end - buf); reserve_used = 0; - core_shrink(handle, dircache_root, allocated_size); + core_shrink(dircache_handle, dircache_root, allocated_size); return res; fail: dircache_disable(); - core_free(handle); + core_free(dircache_handle); return res; } @@ -928,7 +982,9 @@ void* dircache_steal_buffer(size_t *size) *size = 0; return NULL; } - + + /* since we give up the buffer (without freeing), it must not move anymore */ + dont_move = true; *size = dircache_size + (DIRCACHE_RESERVE-reserve_used); return dircache_root; diff --git a/firmware/core_alloc.c b/firmware/core_alloc.c index 9a4388cbf..018755578 100644 --- a/firmware/core_alloc.c +++ b/firmware/core_alloc.c @@ -6,7 +6,7 @@ /* not static so it can be discovered by core_get_data() */ struct buflib_context core_ctx; - +static int debug_handle; void core_allocator_init(void) { buffer_init(); @@ -14,6 +14,18 @@ void core_allocator_init(void) void *start = buffer_get_buffer(&size); buflib_init(&core_ctx, start, size); buffer_release_buffer(size); + + /* REMOVE THIS USELESS CRAP */ + debug_handle = core_alloc("debug", 2<<10); +} + +void free_debug(void) +{ + if (debug_handle > 0) + { + core_free(debug_handle); + debug_handle = 0; + } } int core_alloc(const char* name, size_t size) diff --git a/firmware/include/core_alloc.h b/firmware/include/core_alloc.h index 4ea7e9a09..603e24ccc 100644 --- a/firmware/include/core_alloc.h +++ b/firmware/include/core_alloc.h @@ -14,6 +14,7 @@ int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops); int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops); bool core_shrink(int handle, void* new_start, size_t new_size); void core_free(int handle); +void free_debug(void); size_t core_available(void); /* DO NOT ADD wrappers for buflib_buffer_out/in. They do not call -- 2.11.4.GIT