1
0
Fork 0
forked from len0rd/rockbox

playlist: Refactor control cache flush

Make background control file flushing work on non-dircache targets.
It has nothing to do with dircache and doesn't belong in the dircache
scan thread.

Change-Id: I3f39261ccaebb5dce69e7db3d2e0c0c0c54f640b
This commit is contained in:
Aidan MacDonald 2023-01-21 18:18:37 +00:00
parent 551e6aac50
commit 2a40d42012

View file

@ -368,26 +368,40 @@ static void sync_control(struct playlist_info* playlist, bool force)
} }
} }
static int flush_cached_control_unlocked(struct playlist_info* playlist);
#if USING_STORAGE_CALLBACK
static void flush_control_cache_idle_cb(unsigned short id, void *ev, void *ud)
{
(void)id;
(void)ev;
struct playlist_info *playlist = ud;
playlist_mutex_lock(&playlist->mutex);
if (playlist->control_fd >= 0)
flush_cached_control_unlocked(playlist);
playlist_mutex_unlock(&playlist->mutex);
}
#endif
/* /*
* Flush any cached control commands to disk. Called when playlist is being * Flush any cached control commands to disk. Called when playlist is being
* modified. Returns 0 on success and -1 on failure. * modified. Returns 0 on success and -1 on failure.
*/ */
static int flush_cached_control(struct playlist_info* playlist) static int flush_cached_control_unlocked(struct playlist_info* playlist)
{ {
int result = 0; int result = 0;
int i;
if (playlist->num_cached <= 0) if (playlist->num_cached <= 0)
return 0; return 0;
playlist_mutex_lock(&(playlist->mutex));
lseek(playlist->control_fd, 0, SEEK_END); lseek(playlist->control_fd, 0, SEEK_END);
for (i=0; i<playlist->num_cached; i++) for (int i = 0; i < playlist->num_cached; i++)
{ {
struct playlist_control_cache* cache = &(playlist->control_cache[i]); struct playlist_control_cache* cache = &playlist->control_cache[i];
switch (cache->command) switch (cache->command)
{ {
case PLAYLIST_COMMAND_PLAYLIST: case PLAYLIST_COMMAND_PLAYLIST:
@ -432,16 +446,24 @@ static int flush_cached_control(struct playlist_info* playlist)
{ {
playlist->num_cached = 0; playlist->num_cached = 0;
playlist->pending_control_sync = true; playlist->pending_control_sync = true;
result = 0; result = 0;
} }
else else
{ {
/* At this point the control file is likely corrupted. We still
* need to clear the cache to avoid a buffer overflow from the
* next command. It's unsafe to splash() because this function
* can be called off the main thread.
*
* TODO: recover from failed playlist control file writes.
*/
playlist->num_cached = 0;
result = -1; result = -1;
splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
} }
playlist_mutex_unlock(&(playlist->mutex)); #if USING_STORAGE_CALLBACK
remove_event_ex(DISK_EVENT_SPINUP, flush_control_cache_idle_cb, playlist);
#endif
return result; return result;
} }
@ -457,9 +479,8 @@ static int update_control(struct playlist_info* playlist,
struct playlist_control_cache* cache; struct playlist_control_cache* cache;
bool flush = false; bool flush = false;
playlist_mutex_lock(&(playlist->mutex)); playlist_mutex_lock(&playlist->mutex);
cache = &playlist->control_cache[playlist->num_cached++];
cache = &(playlist->control_cache[playlist->num_cached++]);
cache->command = command; cache->command = command;
cache->i1 = i1; cache->i1 = i1;
@ -473,23 +494,26 @@ static int update_control(struct playlist_info* playlist,
case PLAYLIST_COMMAND_PLAYLIST: case PLAYLIST_COMMAND_PLAYLIST:
case PLAYLIST_COMMAND_ADD: case PLAYLIST_COMMAND_ADD:
case PLAYLIST_COMMAND_QUEUE: case PLAYLIST_COMMAND_QUEUE:
#ifndef HAVE_DIRCACHE /*
case PLAYLIST_COMMAND_DELETE: * These commands can use s1/s2, which may point to
case PLAYLIST_COMMAND_RESET: * stack allocated buffers, so flush them immediately.
#endif */
flush = true; flush = true;
break; break;
case PLAYLIST_COMMAND_SHUFFLE: default:
case PLAYLIST_COMMAND_UNSHUFFLE:
default: /* only flush when needed */
break; break;
} }
if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE) if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
result = flush_cached_control(playlist); result = flush_cached_control_unlocked(playlist);
else
playlist_mutex_unlock(&(playlist->mutex)); {
#if USING_STORAGE_CALLBACK
add_event_ex(DISK_EVENT_SPINUP, true, flush_control_cache_idle_cb, playlist);
#endif
}
playlist_mutex_unlock(&playlist->mutex);
return result; return result;
} }
@ -1885,30 +1909,16 @@ static int get_next_index(const struct playlist_info* playlist, int steps,
} }
#ifdef HAVE_DIRCACHE #ifdef HAVE_DIRCACHE
static void dc_flush_playlist_callback(void)
{
struct playlist_info *playlist;
playlist = &current_playlist;
if (playlist->control_fd >= 0)
{
flush_cached_control(playlist);
sync_control(playlist, true);
}
else if (playlist->control_fd < 0 || playlist->num_cached <= 0)
unregister_storage_idle_func(dc_flush_playlist_callback, false);
}
/** /**
* Thread to update filename pointers to dircache on background * Thread to update filename pointers to dircache on background
* without affecting playlist load up performance. This thread also flushes * without affecting playlist load up performance.
* any pending control commands when the disk spins up.
*/ */
static void dc_thread_playlist(void) static void dc_thread_playlist(void)
{ {
struct queue_event ev; struct queue_event ev;
static char tmp[MAX_PATH+1]; static char tmp[MAX_PATH+1];
struct playlist_info *playlist; struct playlist_info *playlist = &current_playlist;
struct dircache_fileref *dcfrefs; struct dircache_fileref *dcfrefs;
int index; int index;
int seek; int seek;
@ -1941,13 +1951,6 @@ static void dc_thread_playlist(void)
timeout or 5s, whichever is less */ timeout or 5s, whichever is less */
case SYS_TIMEOUT: case SYS_TIMEOUT:
{ {
playlist = &current_playlist;
if (playlist->control_fd >= 0)
{
if (playlist->num_cached > 0)
register_storage_idle_func(dc_flush_playlist_callback);
}
if (!playlist->dcfrefs_handle || playlist->amount <= 0) if (!playlist->dcfrefs_handle || playlist->amount <= 0)
break ; break ;
@ -2110,13 +2113,15 @@ void playlist_init(void)
void playlist_shutdown(void) void playlist_shutdown(void)
{ {
struct playlist_info* playlist = &current_playlist; struct playlist_info* playlist = &current_playlist;
playlist_mutex_lock(&playlist->mutex);
if (playlist->control_fd >= 0) if (playlist->control_fd >= 0)
{ {
flush_cached_control(playlist); flush_cached_control_unlocked(playlist);
close_playlist_control_file(playlist); close_playlist_control_file(playlist);
} }
playlist_mutex_unlock(&playlist->mutex);
} }
/* /*