diff options
author | Eric Wong <normalperson@yhbt.net> | 2008-10-12 05:28:25 -0700 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2008-10-12 05:29:27 -0700 |
commit | 7278e3b2ee49a853b01dc6532bd7067a264f235d (patch) | |
tree | 28e83de310f0c58df226e253a82b0d43ff784497 | |
parent | c87ce02575e0a9d5ac3e50938688187ea28ad400 (diff) | |
parent | c7579ca2d8422f0172537e1ca7d1bd46edfc4f9d (diff) | |
download | mpd-7278e3b2ee49a853b01dc6532bd7067a264f235d.tar.gz mpd-7278e3b2ee49a853b01dc6532bd7067a264f235d.tar.xz mpd-7278e3b2ee49a853b01dc6532bd7067a264f235d.zip |
Merge branch 'ew/directory'
* ew/directory: (21 commits)
update: fix multiple deletes from *vec iterators
directory: children leave parents before being free()ed
directory: always maintain sorted properties vectors
update: simplify the serialized_delete usage a bit
update: remove delete_each_song and clear_directory
directory: directory_free kills all that it contains
update: serialize directory deletions
update: serialize song_free in main thread
dirvec: introduce locking for all iterators
dirvec: use dirvec_for_each where it makes sense
dirvec: add dirvec_for_each iterator
songvec: avoid holding nr_lock during free(3)
update: allow music_root updates to be queued
update: validate in command.c and fix small memory leak
directory: rename isRootDirectory => path_is_music_root
Avoid calling isRootDirectory when we have a directory object
directory: make music_root global and avoid runtime initialization
directory: use mpd_sizeof_str_flex_array for path, too
tag_item: avoid wasting space when struct is unpackable
song: use mpd_sizeof_str_flex_array for song.url
...
[ew: fixed up merge errors with myself when isRootDirectory
went away]
-rw-r--r-- | src/command.c | 14 | ||||
-rw-r--r-- | src/database.c | 30 | ||||
-rw-r--r-- | src/dbUtils.c | 4 | ||||
-rw-r--r-- | src/directory.c | 91 | ||||
-rw-r--r-- | src/directory.h | 15 | ||||
-rw-r--r-- | src/directory_print.c | 22 | ||||
-rw-r--r-- | src/directory_save.c | 4 | ||||
-rw-r--r-- | src/dirvec.c | 67 | ||||
-rw-r--r-- | src/dirvec.h | 5 | ||||
-rw-r--r-- | src/gcc.h | 5 | ||||
-rw-r--r-- | src/song.c | 4 | ||||
-rw-r--r-- | src/song.h | 4 | ||||
-rw-r--r-- | src/songvec.c | 29 | ||||
-rw-r--r-- | src/songvec.h | 2 | ||||
-rw-r--r-- | src/tag.h | 2 | ||||
-rw-r--r-- | src/tag_pool.c | 4 | ||||
-rw-r--r-- | src/update.c | 107 |
17 files changed, 230 insertions, 179 deletions
diff --git a/src/command.c b/src/command.c index 518bf5f0d..04e85c6fe 100644 --- a/src/command.c +++ b/src/command.c @@ -571,7 +571,7 @@ static int handleLsInfo(int fd, mpd_unused int *permission, directory_print(fd, dir); - if (isRootDirectory(path)) + if (dir == &music_root) return lsPlaylists(fd, path); return 0; @@ -802,9 +802,15 @@ static int handleUpdate(int fd, mpd_unused int *permission, char *path = NULL; assert(argc <= 2); - if (argc == 2 && !(path = sanitizePathDup(argv[1]))) { - commandError(fd, ACK_ERROR_ARG, "invalid path"); - return -1; + if (argc == 2) { + if (!(path = sanitizePathDup(argv[1]))) { + commandError(fd, ACK_ERROR_ARG, "invalid path"); + return -1; + } + if (path_is_music_root(path)) { + free(path); + path = NULL; + } } return print_update_result(fd, directory_update_init(path)); } diff --git a/src/database.c b/src/database.c index dde57ce6a..733180fea 100644 --- a/src/database.c +++ b/src/database.c @@ -32,14 +32,10 @@ #include "os_compat.h" #include "myfprintf.h" -static struct directory *music_root; - static time_t directory_dbModTime; void db_init(void) { - music_root = directory_new("", NULL); - if (!directory_update_init(NULL)) FATAL("directory update failed\n"); @@ -54,22 +50,12 @@ void db_init(void) void db_finish(void) { - directory_free(music_root); -} - -struct directory * db_get_root(void) -{ - assert(music_root != NULL); - - return music_root; + directory_free(&music_root); } struct directory * db_get_directory(const char *name) { - if (name == NULL) - return music_root; - - return directory_get_subdir(music_root, name); + return name ? directory_get_subdir(&music_root, name) : &music_root; } struct mpd_song *db_get_song(const char *file) @@ -195,11 +181,7 @@ int db_save(void) struct stat st; DEBUG("removing empty directories from DB\n"); - directory_prune_empty(music_root); - - DEBUG("sorting DB\n"); - - directory_sort(music_root); + directory_prune_empty(&music_root); DEBUG("writing DB\n"); @@ -220,7 +202,7 @@ int db_save(void) DIRECTORY_FS_CHARSET "%s\n" DIRECTORY_INFO_END "\n", getFsCharset()); - if (directory_save(fd, music_root) < 0) { + if (directory_save(fd, &music_root) < 0) { ERROR("Failed to write to database file: %s\n", strerror(errno)); xclose(fd); @@ -243,8 +225,6 @@ int db_load(void) int foundFsCharset = 0; int foundVersion = 0; - if (!music_root) - music_root = directory_new("", NULL); while (!(fp = fopen(dbFile, "r")) && errno == EINTR) ; if (fp == NULL) { ERROR("unable to open db file \"%s\": %s\n", @@ -296,7 +276,7 @@ int db_load(void) DEBUG("reading DB\n"); - directory_load(fp, music_root); + directory_load(fp, &music_root); while (fclose(fp) && errno == EINTR) ; stats.numberOfSongs = countSongsIn(NULL); diff --git a/src/dbUtils.c b/src/dbUtils.c index 1fadb232e..418020d9e 100644 --- a/src/dbUtils.c +++ b/src/dbUtils.c @@ -57,7 +57,7 @@ static int countSongsInDirectory(struct directory *dir, void *data) static int printDirectoryInDirectory(struct directory *dir, void *data) { int fd = (int)(size_t)data; - if (!isRootDirectory(dir->path)) + if (dir != &music_root) fdprintf(fd, "directory: %s\n", directory_get_path(dir)); return 0; } @@ -339,7 +339,7 @@ static int sumSavedFilenameMemoryInDirectory(struct directory *dir, void *data) { int *sum = data; - if (!isRootDirectory(dir->path)) + if (dir != &music_root) return 0; *sum += (strlen(directory_get_path(dir)) + 1 diff --git a/src/directory.c b/src/directory.c index b90b477fd..40be57855 100644 --- a/src/directory.c +++ b/src/directory.c @@ -23,42 +23,61 @@ #include "myfprintf.h" #include "dirvec.h" -struct directory * directory_new(const char *dirname, struct directory * parent) +struct directory music_root; + +struct directory * directory_new(const char *path, struct directory * parent) { struct directory *dir; + size_t pathlen; - assert(dirname != NULL); - assert((*dirname == 0) == (parent == NULL)); + assert(path); + assert(*path); + assert(parent); - dir = xcalloc(1, sizeof(struct directory)); - dir->path = xstrdup(dirname); + pathlen = strlen(path); + dir = xcalloc(1, sizeof(*dir) - sizeof(dir->path) + pathlen + 1); + memcpy(dir->path, path, pathlen + 1); dir->parent = parent; return dir; } +static int free_each_song(struct mpd_song *song, mpd_unused void *arg) +{ + song_free(song); + return 0; +} + +static int free_each_dir(struct directory *dir, void *arg) +{ + if (arg != dir) + directory_free(dir); + return 0; +} + void directory_free(struct directory *dir) { + directory_walk(dir, free_each_song, free_each_dir, dir); dirvec_destroy(&dir->children); songvec_destroy(&dir->songs); - free(dir->path); - free(dir); - /* this resets last dir returned */ - /*directory_get_path(NULL); */ + if (dir != &music_root) { + assert(dir->parent); + dirvec_delete(&dir->parent->children, dir); + free(dir); + } } -void directory_prune_empty(struct directory *dir) +static int dir_pruner(struct directory *dir, void *_dv) { - int i; - struct dirvec *dv = &dir->children; + directory_prune_empty(dir); + if (directory_is_empty(dir)) + dirvec_delete((struct dirvec *)_dv, dir); + return 0; +} - for (i = dv->nr; --i >= 0; ) { - directory_prune_empty(dv->base[i]); - if (directory_is_empty(dv->base[i])) - dirvec_delete(dv, dv->base[i]); - } - if (!dv->nr) - dirvec_destroy(dv); +void directory_prune_empty(struct directory *dir) +{ + dirvec_for_each(&dir->children, dir_pruner, &dir->children); } struct directory * @@ -71,7 +90,7 @@ directory_get_subdir(struct directory *dir, const char *name) assert(name != NULL); - if (isRootDirectory(name)) + if (path_is_music_root(name)) return dir; duplicated = xstrdup(name); @@ -94,27 +113,25 @@ directory_get_subdir(struct directory *dir, const char *name) return found; } -void directory_sort(struct directory *dir) -{ - int i; - struct dirvec *dv = &dir->children; +struct dirwalk_arg { + int (*each_song) (struct mpd_song *, void *); + int (*each_dir) (struct directory *, void *); + void *data; +}; - dirvec_sort(dv); - songvec_sort(&dir->songs); +static int dirwalk_x(struct directory *dir, void *_arg) +{ + struct dirwalk_arg *arg = _arg; - for (i = dv->nr; --i >= 0; ) - directory_sort(dv->base[i]); + return directory_walk(dir, arg->each_song, arg->each_dir, arg->data); } -int -directory_walk(struct directory *dir, +int directory_walk(struct directory *dir, int (*forEachSong) (struct mpd_song *, void *), int (*forEachDir) (struct directory *, void *), void *data) { - struct dirvec *dv = &dir->children; int err = 0; - size_t j; if (forEachDir && (err = forEachDir(dir, data)) < 0) return err; @@ -125,9 +142,13 @@ directory_walk(struct directory *dir, return err; } - for (j = 0; err >= 0 && j < dv->nr; ++j) - err = directory_walk(dv->base[j], forEachSong, - forEachDir, data); + if (forEachDir) { + struct dirwalk_arg dw_arg; + dw_arg.each_song = forEachSong; + dw_arg.each_dir = forEachDir; + dw_arg.data = data; + err = dirvec_for_each(&dir->children, dirwalk_x, &dw_arg); + } return err; } diff --git a/src/directory.h b/src/directory.h index 949df0c0e..8f0d797a8 100644 --- a/src/directory.h +++ b/src/directory.h @@ -22,6 +22,7 @@ #include "song.h" #include "dirvec.h" #include "songvec.h" +#include "gcc.h" #define DIRECTORY_DIR "directory: " #define DIRECTORY_MTIME "mtime: " /* DEPRECATED, noop-read-only */ @@ -33,19 +34,21 @@ #define DIRECTORY_FS_CHARSET "fs_charset: " struct directory { - char *path; struct dirvec children; struct songvec songs; struct directory *parent; ino_t inode; dev_t device; unsigned stat; /* not needed if ino_t == dev_t == 0 is impossible */ -}; + char path[mpd_sizeof_str_flex_array]; +} mpd_packed; -static inline int isRootDirectory(const char *name) +extern struct directory music_root; + +static inline int path_is_music_root(const char *name) { - /* TODO: verify and remove !name check */ - return (!name || *name == '\0' || !strcmp(name, "/")); + assert(name); + return (!*name || !strcmp(name, "/")); } struct directory * directory_new(const char *dirname, struct directory *parent); @@ -89,8 +92,6 @@ int directory_save(int fd, struct directory *dir); void directory_load(FILE *fp, struct directory *dir); -void directory_sort(struct directory *dir); - int db_walk(const char *name, int (*forEachSong) (struct mpd_song *, void *), int (*forEachDir) (struct directory *, void *), void *data); diff --git a/src/directory_print.c b/src/directory_print.c index 1c30f1608..d7b42ec59 100644 --- a/src/directory_print.c +++ b/src/directory_print.c @@ -23,25 +23,23 @@ #include "songvec.h" #include "myfprintf.h" -static int dirvec_print(int fd, const struct dirvec *dv) +static int directory_print_info(struct directory *dir, int fd) { - size_t i; - - for (i = 0; i < dv->nr; ++i) { - if (fdprintf(fd, DIRECTORY_DIR "%s\n", - directory_get_path(dv->base[i])) < 0) - return -1; - } + return fdprintf(fd, DIRECTORY_DIR "%s\n", directory_get_path(dir)); +} - return 0; +static int directory_print_info_x(struct directory *dir, void *data) +{ + return directory_print_info(dir, (int)(size_t)data); } int directory_print(int fd, const struct directory *dir) { - if (dirvec_print(fd, &dir->children) < 0) + void *arg = (void *)(size_t)fd; + + if (dirvec_for_each(&dir->children, directory_print_info_x, arg) < 0) return -1; - if (songvec_for_each(&dir->songs, song_print_info_x, - (void *)(size_t)fd) < 0) + if (songvec_for_each(&dir->songs, song_print_info_x, arg) < 0) return -1; return 0; } diff --git a/src/directory_save.c b/src/directory_save.c index c39ece58a..e12de0019 100644 --- a/src/directory_save.c +++ b/src/directory_save.c @@ -45,7 +45,7 @@ int directory_save(int fd, struct directory *dir) struct dirvec *children = &dir->children; size_t i; - if (!isRootDirectory(dir->path) && + if ((dir != &music_root) && fdprintf(fd, DIRECTORY_BEGIN "%s\n", directory_get_path(dir)) < 0) return -1; @@ -70,7 +70,7 @@ int directory_save(int fd, struct directory *dir) if (fdprintf(fd, SONG_END "\n") < 0) return -1; - if (!isRootDirectory(dir->path) && + if ((dir != &music_root) && fdprintf(fd, DIRECTORY_END "%s\n", directory_get_path(dir)) < 0) return -1; diff --git a/src/dirvec.c b/src/dirvec.c index fdfbb3434..a5eb6d54e 100644 --- a/src/dirvec.c +++ b/src/dirvec.c @@ -3,6 +3,8 @@ #include "os_compat.h" #include "utils.h" +static pthread_mutex_t nr_lock = PTHREAD_MUTEX_INITIALIZER; + static size_t dv_size(struct dirvec *dv) { return dv->nr * sizeof(struct directory *); @@ -16,55 +18,92 @@ static int dirvec_cmp(const void *d1, const void *d2) return strcmp(a->path, b->path); } -void dirvec_sort(struct dirvec *dv) -{ - qsort(dv->base, dv->nr, sizeof(struct directory *), dirvec_cmp); -} - struct directory *dirvec_find(const struct dirvec *dv, const char *path) { int i; + struct directory *ret = NULL; - for (i = dv->nr; --i >= 0; ) - if (!strcmp(dv->base[i]->path, path)) - return dv->base[i]; - return NULL; + pthread_mutex_lock(&nr_lock); + for (i = dv->nr; --i >= 0; ) { + if (strcmp(dv->base[i]->path, path)) + continue; + ret = dv->base[i]; + break; + } + pthread_mutex_unlock(&nr_lock); + return ret; } int dirvec_delete(struct dirvec *dv, struct directory *del) { int i; + pthread_mutex_lock(&nr_lock); for (i = dv->nr; --i >= 0; ) { if (dv->base[i] != del) continue; /* we _don't_ call directory_free() here */ if (!--dv->nr) { + pthread_mutex_unlock(&nr_lock); free(dv->base); dv->base = NULL; + return i; } else { memmove(&dv->base[i], &dv->base[i + 1], (dv->nr - i + 1) * sizeof(struct directory *)); dv->base = xrealloc(dv->base, dv_size(dv)); } - return i; + break; } + pthread_mutex_unlock(&nr_lock); - return -1; /* not found */ + return i; } void dirvec_add(struct dirvec *dv, struct directory *add) { - ++dv->nr; + size_t old_nr; + + pthread_mutex_lock(&nr_lock); + old_nr = dv->nr++; dv->base = xrealloc(dv->base, dv_size(dv)); - dv->base[dv->nr - 1] = add; + dv->base[old_nr] = add; + if (old_nr && dirvec_cmp(&dv->base[old_nr - 1], &add) >= 0) + qsort(dv->base, dv->nr, sizeof(struct directory *), dirvec_cmp); + pthread_mutex_unlock(&nr_lock); } void dirvec_destroy(struct dirvec *dv) { + pthread_mutex_lock(&nr_lock); + dv->nr = 0; + pthread_mutex_unlock(&nr_lock); if (dv->base) { free(dv->base); dv->base = NULL; } - dv->nr = 0; +} + +int dirvec_for_each(const struct dirvec *dv, + int (*fn)(struct directory *, void *), void *arg) +{ + size_t i; + size_t prev_nr; + + pthread_mutex_lock(&nr_lock); + for (i = 0; i < dv->nr; ) { + struct directory *dir = dv->base[i]; + + assert(dir); + prev_nr = dv->nr; + pthread_mutex_unlock(&nr_lock); + if (fn(dir, arg) < 0) + return -1; + pthread_mutex_lock(&nr_lock); /* dv->nr may change in fn() */ + if (prev_nr == dv->nr) + ++i; + } + pthread_mutex_unlock(&nr_lock); + + return 0; } diff --git a/src/dirvec.h b/src/dirvec.h index 02496cd2b..6709537b8 100644 --- a/src/dirvec.h +++ b/src/dirvec.h @@ -8,8 +8,6 @@ struct dirvec { size_t nr; }; -void dirvec_sort(struct dirvec *dv); - struct directory *dirvec_find(const struct dirvec *dv, const char *path); int dirvec_delete(struct dirvec *dv, struct directory *del); @@ -23,4 +21,7 @@ static inline void dirvec_clear(struct dirvec *dv) void dirvec_destroy(struct dirvec *dv); +int dirvec_for_each(const struct dirvec *dv, + int (*fn)(struct directory *, void *), void *arg); + #endif /* DIRVEC_H */ @@ -32,6 +32,10 @@ # define mpd_must_check __attribute__ ((warn_unused_result)) # define mpd_noreturn __attribute__ ((noreturn)) # define mpd_packed __attribute__ ((packed)) + +/* str_flex_array is always >= 1 because we always 0 terminate strings */ +# define mpd_sizeof_str_flex_array 1 + /* these are very useful for type checking */ # define mpd_printf __attribute__ ((format(printf,1,2))) # define mpd_fprintf __attribute__ ((format(printf,2,3))) @@ -52,6 +56,7 @@ # define mpd_must_check # define mpd_noreturn # define mpd_packed +# define mpd_sizeof_str_flex_array (sizeof(size_t)) # define mpd_printf # define mpd_fprintf # define mpd_fprintf_ diff --git a/src/song.c b/src/song.c index 77d113fd4..a524dca93 100644 --- a/src/song.c +++ b/src/song.c @@ -85,7 +85,7 @@ void song_free(struct mpd_song * song) ssize_t song_print_url(struct mpd_song *song, int fd) { - if (song->parent && !isRootDirectory(song->parent->path)) + if (song->parent && song->parent != &music_root) return fdprintf(fd, "%s%s/%s\n", SONG_FILE, directory_get_path(song->parent), song->url); return fdprintf(fd, "%s%s\n", SONG_FILE, song->url); @@ -240,7 +240,7 @@ char *song_get_url(struct mpd_song *song, char *path_max_tmp) assert(*song->url); - if (!song->parent || isRootDirectory(song->parent->path)) + if (!song->parent || song->parent == &music_root) strcpy(path_max_tmp, song->url); else pfx_dir(path_max_tmp, song->url, strlen(song->url), diff --git a/src/song.h b/src/song.h index 338bcf6c1..e29c034ef 100644 --- a/src/song.h +++ b/src/song.h @@ -35,8 +35,8 @@ struct mpd_song { struct mpd_tag *tag; struct directory *parent; time_t mtime; - char url[sizeof(size_t)]; -}; + char url[mpd_sizeof_str_flex_array]; +} mpd_packed; void song_free(struct mpd_song *); diff --git a/src/songvec.c b/src/songvec.c index ce04a4373..19433a222 100644 --- a/src/songvec.c +++ b/src/songvec.c @@ -17,13 +17,6 @@ static size_t sv_size(struct songvec *sv) return sv->nr * sizeof(struct mpd_song *); } -void songvec_sort(struct songvec *sv) -{ - pthread_mutex_lock(&nr_lock); - qsort(sv->base, sv->nr, sizeof(struct mpd_song *), songvec_cmp); - pthread_mutex_unlock(&nr_lock); -} - struct mpd_song *songvec_find(const struct songvec *sv, const char *url) { int i; @@ -48,10 +41,12 @@ int songvec_delete(struct songvec *sv, const struct mpd_song *del) for (i = sv->nr; --i >= 0; ) { if (sv->base[i] != del) continue; - /* we _don't_ call freeSong() here */ + /* we _don't_ call song_free() here */ if (!--sv->nr) { + pthread_mutex_unlock(&nr_lock); free(sv->base); sv->base = NULL; + return i; } else { memmove(&sv->base[i], &sv->base[i + 1], (sv->nr - i + 1) * sizeof(struct mpd_song *)); @@ -66,40 +61,48 @@ int songvec_delete(struct songvec *sv, const struct mpd_song *del) void songvec_add(struct songvec *sv, struct mpd_song *add) { + size_t old_nr; + pthread_mutex_lock(&nr_lock); - ++sv->nr; + old_nr = sv->nr++; sv->base = xrealloc(sv->base, sv_size(sv)); - sv->base[sv->nr - 1] = add; + sv->base[old_nr] = add; + if (old_nr && songvec_cmp(&sv->base[old_nr - 1], &add) >= 0) + qsort(sv->base, sv->nr, sizeof(struct mpd_song *), songvec_cmp); pthread_mutex_unlock(&nr_lock); } void songvec_destroy(struct songvec *sv) { pthread_mutex_lock(&nr_lock); + sv->nr = 0; + pthread_mutex_unlock(&nr_lock); if (sv->base) { free(sv->base); sv->base = NULL; } - sv->nr = 0; - pthread_mutex_unlock(&nr_lock); } int songvec_for_each(const struct songvec *sv, int (*fn)(struct mpd_song *, void *), void *arg) { size_t i; + size_t prev_nr; pthread_mutex_lock(&nr_lock); - for (i = 0; i < sv->nr; ++i) { + for (i = 0; i < sv->nr; ) { struct mpd_song *song = sv->base[i]; assert(song); assert(*song->url); + prev_nr = sv->nr; pthread_mutex_unlock(&nr_lock); /* fn() may block */ if (fn(song, arg) < 0) return -1; pthread_mutex_lock(&nr_lock); /* sv->nr may change in fn() */ + if (prev_nr == sv->nr) + ++i; } pthread_mutex_unlock(&nr_lock); diff --git a/src/songvec.h b/src/songvec.h index 633cf8d66..10a896052 100644 --- a/src/songvec.h +++ b/src/songvec.h @@ -9,8 +9,6 @@ struct songvec { size_t nr; }; -void songvec_sort(struct songvec *sv); - struct mpd_song *songvec_find(const struct songvec *sv, const char *url); int songvec_delete(struct songvec *sv, const struct mpd_song *del); @@ -42,7 +42,7 @@ extern const char *mpdTagItemKeys[]; struct tag_item { enum tag_type type; - char value[1]; + char value[mpd_sizeof_str_flex_array]; } mpd_packed; struct mpd_tag { diff --git a/src/tag_pool.c b/src/tag_pool.c index d227a2988..1d92502cc 100644 --- a/src/tag_pool.c +++ b/src/tag_pool.c @@ -67,7 +67,9 @@ static struct slot *slot_alloc(struct slot *next, enum tag_type type, const char *value, int length) { - struct slot *slot = xmalloc(sizeof(*slot) + length); + struct slot *slot; + + slot = xmalloc(sizeof(*slot) - sizeof(slot->item.value) + length + 1); slot->next = next; slot->ref = 1; slot->item.type = type; diff --git a/src/update.c b/src/update.c index ca3640b73..9ede72759 100644 --- a/src/update.c +++ b/src/update.c @@ -46,7 +46,9 @@ static const unsigned update_task_id_max = 1 << 15; static unsigned update_task_id; -static struct mpd_song *delete; +static void (*delete_fn)(void *); + +static void *delete; static struct condition delete_cond; @@ -62,44 +64,33 @@ static void directory_set_stat(struct directory *dir, const struct stat *st) dir->stat = 1; } -static void delete_song(struct directory *dir, struct mpd_song *del) +static void serialized_delete(void (*fn)(void *), void *del) { - /* first, prevent traversers in main task from getting this */ - songvec_delete(&dir->songs, del); - - /* now take it out of the playlist (in the main_task) */ cond_enter(&delete_cond); assert(!delete); + assert(!delete_fn); + delete_fn = fn; delete = del; wakeup_main_task(); do { cond_wait(&delete_cond); } while (delete); cond_leave(&delete_cond); - - /* finally, all possible references gone, free it */ - song_free(del); } -static int delete_each_song(struct mpd_song *song, mpd_unused void *data) +static void delete_song_safely(struct mpd_song *song) { - struct directory *dir = data; - assert(song->parent == dir); - delete_song(dir, song); - return 0; + char tmp[MPD_PATH_MAX]; + LOG("removing: %s\n", song_get_url(song, tmp)); + deleteASongFromPlaylist(song); + song_free(song); } -/** - * Recursively remove all sub directories and songs from a directory, - * leaving an empty directory. - */ -static void clear_directory(struct directory *dir) +static void delete_song(struct directory *dir, struct mpd_song *del) { - int i; - - for (i = dir->children.nr; --i >= 0;) - clear_directory(dir->children.base[i]); - dirvec_clear(&dir->children); + /* first, prevent traversers in main task from getting this */ + songvec_delete(&dir->songs, del); - songvec_for_each(&dir->songs, delete_each_song, dir); + /* now take it out of the playlist (in the main_task) and free */ + serialized_delete((void (*)(void *))delete_song_safely, del); } /** @@ -109,9 +100,11 @@ static void delete_directory(struct directory *dir) { assert(dir->parent != NULL); - clear_directory(dir); + /* first, prevent traversers in main task from getting this */ dirvec_delete(&dir->parent->children, dir); - directory_free(dir); + + /* now we let the main task recursively delete everything under us */ + serialized_delete((void (*)(void *))directory_free, dir); } struct delete_data { @@ -149,23 +142,27 @@ static void delete_path(const char *path) } } -static void -removeDeletedFromDirectory(char *path_max_tmp, struct directory *dir) +/* passed to dirvec_for_each */ +static int delete_directory_if_removed(struct directory *dir, void *_data) { - int i; - struct dirvec *dv = &dir->children; - struct delete_data data; + if (!isDir(dir->path)) { + struct delete_data *data = _data; - for (i = dv->nr; --i >= 0; ) { - if (isDir(dv->base[i]->path)) - continue; - LOG("removing directory: %s\n", dv->base[i]->path); - dirvec_delete(dv, dv->base[i]); + LOG("removing directory: %s\n", directory_get_path(dir)); + dirvec_delete(&data->dir->children, dir); modified = 1; } + return 0; +} + +static void +removeDeletedFromDirectory(char *path_max_tmp, struct directory *dir) +{ + struct delete_data data; data.dir = dir; data.tmp = path_max_tmp; + dirvec_for_each(&dir->children, delete_directory_if_removed, &data); songvec_for_each(&dir->songs, delete_song_if_removed, &data); } @@ -278,7 +275,7 @@ static int updateDirectory(struct directory *dir, const struct stat *st) if (!utf8) continue; - if (!isRootDirectory(dir->path)) + if (dir != &music_root) utf8 = pfx_dir(path_max_tmp, utf8, strlen(utf8), dirname, strlen(dirname)); @@ -317,10 +314,9 @@ directory_make_child_checked(struct directory *parent, const char *path) return dir; } -static struct directory * -addParentPathToDB(const char *utf8path) +static struct directory * addParentPathToDB(const char *utf8path) { - struct directory *dir = db_get_root(); + struct directory *dir = &music_root; char *duplicated = xstrdup(utf8path); char *slash = duplicated; @@ -350,15 +346,17 @@ static void updatePath(const char *utf8path) static void * update_task(void *_path) { - if (_path != NULL && !isRootDirectory(_path)) { - updatePath((char *)_path); - free(_path); + char *utf8path = _path; + + if (utf8path) { + assert(*utf8path); + updatePath(utf8path); + free(utf8path); } else { - struct directory *dir = db_get_root(); struct stat st; - if (myStat(directory_get_path(dir), &st) == 0) - updateDirectory(dir, &st); + if (myStat(directory_get_path(&music_root), &st) == 0) + updateDirectory(&music_root, &st); } if (modified) @@ -388,13 +386,14 @@ unsigned directory_update_init(char *path) { assert(pthread_equal(pthread_self(), main_task)); + assert(!path || (path && *path)); + if (progress != UPDATE_PROGRESS_IDLE) { unsigned next_task_id; - if (!path) - return 0; if (update_paths_nr == ARRAY_SIZE(update_paths)) { - free(path); + if (path) + free(path); return 0; } @@ -416,11 +415,9 @@ void reap_update_task(void) return; cond_enter(&delete_cond); - if (delete) { - char tmp[MPD_PATH_MAX]; - LOG("removing: %s\n", song_get_url(delete, tmp)); - deleteASongFromPlaylist(delete); - delete = NULL; + if (delete && delete_fn) { + delete_fn(delete); + delete = delete_fn = NULL; cond_signal(&delete_cond); } cond_leave(&delete_cond); |