From 22e34030ffb6a743524ef5d6f1e0eceb69d6a5f0 Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Tue, 9 Aug 2011 20:42:05 +0200 Subject: [PATCH 3/3] GSoC/Buflib: Enable compaction in buflib. With this patch, it is possible 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. As an expample, lastfm scrobbling can be toggled dynamically (without the "Reboot to enable" splash). More conversion will follow. --- apps/debug_menu.c | 32 ++++++- apps/dsp.c | 21 ++++- apps/filetree.c | 31 ++++-- apps/filetypes.c | 22 ++++- apps/menus/playback_menu.c | 2 +- apps/playback.c | 79 +++++++++++++-- apps/playlist.c | 95 ++++++++++++------ apps/playlist.h | 9 +- apps/scrobbler.c | 10 ++- apps/tagcache.c | 144 +++++++++++++++++++-------- apps/tagcache.h | 4 +- apps/tagtree.c | 223 ++++++++++++++++++++++++++++++++--------- apps/tagtree.h | 9 +-- apps/talk.c | 26 +++++- apps/tdspeed.c | 44 ++++++++- apps/tree.c | 78 ++++++++++---- apps/tree.h | 36 +++++-- firmware/common/dircache.c | 109 +++++++++++++++------ firmware/core_alloc.c | 14 +++- firmware/include/core_alloc.h | 1 + 20 files changed, 758 insertions(+), 231 deletions(-) diff --git a/apps/debug_menu.c b/apps/debug_menu.c index fb8575e..5a9f0eb 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -425,11 +425,41 @@ 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); + } + 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 a728dd7..423595a 100644 --- a/apps/dsp.c +++ b/apps/dsp.c @@ -318,6 +318,21 @@ 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() */ @@ -329,8 +344,8 @@ void dsp_timestretch_enable(bool enabled) /* 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)); + handle = core_alloc_ex("resample buf", + big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t), &ops); if (handle > 0) big_resample_buf = core_get_data(handle); else @@ -1211,7 +1226,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 1aee80b..35bb2a8 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 c52c734..942ff32 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/menus/playback_menu.c b/apps/menus/playback_menu.c index 1b1a13a..a219373 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/playback.c b/apps/playback.c index a92a9ea..51546d4 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,71 @@ 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); + 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 58c6a92..5e30d95 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 f14b5c6..6dd5535 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/scrobbler.c b/apps/scrobbler.c index 3fe8e60..78414f3 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 78405f7..af66432 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 393a290..6c13efd 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 0d4330b..9297c08 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,68 @@ 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], 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], 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 +273,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 +583,8 @@ static int add_format(const char *buf) { int clause_count = 0; strp++; - + + tagtree_lock(); while (1) { struct tagcache_search_clause *newclause; @@ -529,6 +603,7 @@ static int add_format(const char *buf) clause_count++; } + tagtree_unlock(); formats[format_count]->clause_count = clause_count; } @@ -593,9 +668,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 +696,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 +733,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 +757,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 +778,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 +1082,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 +1121,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 +1267,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 +1332,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 +1369,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 +1412,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 +1425,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init) break; } } + tagtree_unlock(); if (strcmp(tcs.result, UNTAGGED) == 0) { @@ -1337,7 +1436,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 +1453,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 +1493,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 +1502,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 +1527,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 +1542,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 +1553,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 +1676,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 +1745,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 +1795,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 +1820,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 +1905,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 +1919,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 +1934,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 +2001,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 +2020,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 aaf5158..26952b4 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 151a166..d923629 100644 --- a/apps/talk.c +++ b/apps/talk.c @@ -142,6 +142,8 @@ static bool force_enqueue_next; /* enqueue next utterance even if enqueue is fal static int queue_write; /* write index of queue, by application */ static int queue_read; /* read index of queue, by ISR context */ #if CONFIG_CODEC == SWCODEC + /* protects p_thumbnail during during I/O and queue_clip() */ +static int thumbnail_buf_locked; /* protects queue_read, queue_write and thumbnail_buf_used */ static struct mutex queue_mutex SHAREDBSS_ATTR; #define talk_queue_lock() ({ mutex_lock(&queue_mutex); }) @@ -600,7 +602,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 @@ -610,7 +628,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 @@ -890,8 +908,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 */ @@ -909,7 +929,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 2000952..9acaddb 100644 --- a/apps/tdspeed.c +++ b/apps/tdspeed.c @@ -38,9 +38,44 @@ #define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */ +static int32_t** dsp_src; 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; @@ -63,22 +98,22 @@ void tdspeed_init() int handle; if (overlap_buffer[0] == NULL) { - handle = core_alloc("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t)); + handle = core_alloc_ex("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops); overlap_buffer[0] = core_get_data(handle); } if (overlap_buffer[1] == NULL) { - handle = core_alloc("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t)); + handle = core_alloc_ex("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops); overlap_buffer[1] = core_get_data(handle); } if (outbuf[0] == NULL) { - handle = core_alloc("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t)); + handle = core_alloc_ex("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops); outbuf[0] = core_get_data(handle); } if (outbuf[1] == NULL) { - handle = core_alloc("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t)); + handle = core_alloc_ex("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops); outbuf[1] = core_get_data(handle); } } @@ -338,6 +373,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/tree.c b/apps/tree.c index 211ddb2..c7484ff 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 c07b92f..2b29605 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/common/dircache.c b/firmware/common/dircache.c index e737300..bb600b8 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,39 @@ 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; + + 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 +615,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 +651,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 +669,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 +684,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 +705,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 +744,7 @@ int dircache_save(void) return -4; } - + dont_move = false; return 0; } #endif /* HAVE_EEPROM_SETTINGS */ @@ -720,6 +766,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 +787,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 +813,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 +839,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 +898,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,9 +918,9 @@ 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); - dircache_root = core_get_data(handle); - ALIGN_BUFFER(dircache_root, got_size, sizeof(struct dircache_entry)); + dircache_handle = core_alloc_maximum("dircache", &got_size, &ops); + dircache_root = core_get_data(dircache_handle); + ALIGN_BUFFER(dircache_root, got_size, sizeof(struct dircache_entry*)); d_names_start = d_names_end = (char*)dircache_root + got_size - 1; dircache_size = 0; generate_dot_d_names(); @@ -908,11 +957,11 @@ int dircache_build(int last_size) allocated_size = (d_names_end - (char*)dircache_root); 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; } @@ -927,7 +976,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 9a4388c..0187555 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 4ea7e9a..603e24c 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 -- 1.7.5.4