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, return buffer; } +static int bf_action_cb(int action, struct gui_synclist* list) +{ + if (action == ACTION_STD_OK) + { + splash(HZ/1, "Attempting a 64k allocation"); + int handle = core_alloc("test", 64<<10); + splash(HZ/2, (handle > 0) ? "Success":"Fail"); + /* for some reason simplelist doesn't allow adding items here if + * info.get_name is given, so use normal list api */ + gui_synclist_set_nb_items(list, core_get_num_blocks()); + if (handle > 0) + core_free(handle); + action = ACTION_REDRAW; + } + else if (action == ACTION_NONE) + action = ACTION_REDRAW; + return action; +} + static bool dbg_buflib_allocs(void) { struct simplelist_info info; simplelist_info_init(&info, "mem allocs", core_get_num_blocks(), NULL); 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 a728dd75ea..167c043427 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 1aee80b6b2..35bb2a8fd0 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 c52c734a1d..942ff329fe 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 cc9c9e8d8e..07a8bba44c 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 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 { 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 595f943545..0b1413e195 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,81 @@ 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) +{ + 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) +{ + 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 +1376,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 +2733,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) @@ -2923,13 +2987,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(); @@ -2937,10 +2994,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 e35d652ffb..af077e639a 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -732,8 +732,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; @@ -744,8 +742,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: @@ -761,11 +760,6 @@ static void audio_reset_buffer(void) /* Initially set up file buffer as all space available */ size_t allocsize; - if (audiobuf_handle > 0) - audiobuf_handle = core_free(audiobuf_handle); - - audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops); - unsigned char *filebuf = core_get_data(audiobuf_handle); /* Subtract whatever voice needs */ allocsize = talkbuf_init(filebuf); @@ -830,6 +824,70 @@ 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) +{ + 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; + 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_PLAY) == AUDIO_STATUS_PLAY) + { + 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 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, 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 @@ -1378,7 +1378,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) { @@ -1534,9 +1534,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 */ @@ -1941,20 +1970,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) @@ -2404,7 +2436,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'; @@ -2731,6 +2763,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; } @@ -2779,10 +2812,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 } @@ -3358,6 +3391,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; @@ -3380,15 +3414,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; } } @@ -3409,12 +3444,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); @@ -3514,11 +3545,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; } @@ -3534,9 +3566,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) @@ -3552,7 +3584,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 @@ -3568,6 +3599,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) @@ -3586,8 +3618,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 f14b5c6460..6dd5535df1 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 32b77ad287..43d9e03acf 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 d70e5634f9..1d8413f6df 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 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; 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 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[]) 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 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) 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 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 ) 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 a6307d5dd7..78414f3d88 100644 --- a/apps/scrobbler.c +++ b/apps/scrobbler.c @@ -255,6 +255,11 @@ int scrobbler_init(void) return -1; 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 753675f906..5ab77264f6 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)); + 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) - 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); + + 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); @@ -2902,6 +2928,9 @@ static bool commit(void) int masterfd; #ifdef HAVE_DIRCACHE bool dircache_buffer_stolen = false; +#endif +#ifdef HAVE_TC_RAMCACHE + bool ramcache_buffer_stolen = false; #endif bool local_allocation = false; @@ -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(); @@ -3689,9 +3722,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 @@ -3813,6 +3848,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; @@ -3833,7 +3892,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); @@ -3871,12 +3930,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!"); @@ -3887,8 +3947,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); @@ -3919,7 +3978,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; @@ -3962,7 +4023,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. */ @@ -3972,8 +4034,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 @@ -3982,8 +4043,7 @@ static bool load_tagcache(void) if (rc != sizeof(struct index_entry)) { logf("read error #10"); - close(fd); - return false; + goto failure; } idx++; @@ -4010,7 +4070,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; @@ -4022,7 +4082,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; @@ -4032,8 +4092,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. */ @@ -4051,16 +4110,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 */ @@ -4071,15 +4128,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 @@ -4126,8 +4181,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; @@ -4141,8 +4195,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); @@ -4151,7 +4204,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 393a2905f5..6c13efdd0e 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 0d4330bac8..5766d2892e 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 aaf5158e5b..26952b40b7 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/tdspeed.c b/apps/tdspeed.c index 476995a271..69699e5bb4 100644 --- a/apps/tdspeed.c +++ b/apps/tdspeed.c @@ -38,6 +38,46 @@ #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, +}; + + static struct tdspeed_state_s { bool stereo; @@ -51,39 +91,47 @@ static struct tdspeed_state_s int32_t *ovl_buff[2]; /* overlap buffer */ } tdspeed_state; -static int32_t *overlap_buffer[2] = { NULL, NULL }; -static int32_t *outbuf[2] = { NULL, NULL }; - void tdspeed_init(void) { - int handle; - if (!global_settings.timestretch_enabled) return; /* Allocate buffers */ 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) { @@ -390,6 +438,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); 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 @@ #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 211ddb2f9b..c7484ff420 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 c07b92f298..2b296050d3 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 51cf86bf5b..880357ccf4 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 334801ce57..6b2260def3 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 75dfc75b86..2250f5c664 100644 --- a/firmware/core_alloc.c +++ b/firmware/core_alloc.c @@ -6,7 +6,6 @@ /* not static so it can be discovered by core_get_data() */ struct buflib_context core_ctx; - void core_allocator_init(void) { buffer_init();