summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/debug_menu.c21
-rw-r--r--apps/dsp.c60
-rw-r--r--apps/filetree.c31
-rw-r--r--apps/filetypes.c22
-rw-r--r--apps/main.c6
-rw-r--r--apps/menus/playback_menu.c2
-rw-r--r--apps/mpeg.c121
-rw-r--r--apps/playback.c76
-rw-r--r--apps/playlist.c95
-rw-r--r--apps/playlist.h9
-rw-r--r--apps/plugin.c6
-rw-r--r--apps/plugin.h2
-rw-r--r--apps/plugins/imageviewer/imageviewer.c2
-rw-r--r--apps/plugins/mikmod/mikmod.c2
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c2
-rw-r--r--apps/plugins/rockpaint.c2
-rw-r--r--apps/scrobbler.c8
-rw-r--r--apps/tagcache.c144
-rw-r--r--apps/tagcache.h4
-rw-r--r--apps/tagtree.c229
-rw-r--r--apps/tagtree.h9
-rw-r--r--apps/tdspeed.c75
-rw-r--r--apps/tdspeed.h3
-rw-r--r--apps/tree.c78
-rw-r--r--apps/tree.h36
-rw-r--r--firmware/buflib.c5
-rw-r--r--firmware/common/dircache.c114
-rw-r--r--firmware/core_alloc.c1
28 files changed, 866 insertions, 299 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index fb8575ec62..6375094225 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -425,11 +425,32 @@ static const char* bf_getname(int selected_item, void *data,
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 <limits.h>
#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; i<num_files; i++)
{
/* user abort */
@@ -1562,6 +1563,7 @@ static int get_next_dir(char *dir, bool is_forward, bool recursion)
start_dir = NULL;
}
}
+ tree_unlock_cache(tc);
if (!exit)
{
@@ -1612,7 +1614,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
return -2;
}
- files = tc->cache.entries;
+ files = tree_get_entries(tc);
num_files = tc->filesindir;
for (i=0; i<num_files; i++)
@@ -1629,6 +1631,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
if (has_music)
return 0;
+ tree_lock_cache(tc);
if (has_subdir && recurse)
{
for (i=0; i<num_files; i++)
@@ -1647,6 +1650,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
}
}
}
+ tree_unlock_cache(tc);
if (result < 0)
{
@@ -1925,6 +1929,31 @@ static int rotate_index(const struct playlist_info* playlist, int index)
}
/*
+ * Need no movement protection since all 3 allocations are not passed to
+ * other functions which can yield().
+ */
+static int move_callback(int handle, void* current, void* new)
+{
+ (void)handle;
+ struct playlist_info* playlist = &current_playlist;
+ if (current == playlist->indices)
+ 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)
@@ -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 = &current_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));
-
- 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();
@@ -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(&current_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(&current_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(&current_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 <file.h>
#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();