From 4111d84ad5b72f871d35f76a47765c138a201611 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 11 Oct 2008 22:03:05 -0700 Subject: dirvec: add dirvec_for_each iterator This will make it easier to introduce locking --- src/dirvec.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'src/dirvec.c') diff --git a/src/dirvec.c b/src/dirvec.c index fdfbb3434..73375fc70 100644 --- a/src/dirvec.c +++ b/src/dirvec.c @@ -68,3 +68,19 @@ void dirvec_destroy(struct dirvec *dv) } dv->nr = 0; } + +int dirvec_for_each(const struct dirvec *dv, + int (*fn)(struct directory *, void *), void *arg) +{ + size_t i; + + for (i = 0; i < dv->nr; ++i) { + struct directory *dir = dv->base[i]; + + assert(dir); + if (fn(dir, arg) < 0) + return -1; + } + + return 0; +} -- cgit v1.2.3 From ae8e81043dcf2169f4fb137a74df9926ed248f38 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 11 Oct 2008 23:23:09 -0700 Subject: dirvec: introduce locking for all iterators Like the songvec nr_lock, only one lock is used for all traversals since they're rarely changed. This only projects traversals, but not the individual structures themselves. --- src/dirvec.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) (limited to 'src/dirvec.c') diff --git a/src/dirvec.c b/src/dirvec.c index 73375fc70..228c54be9 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 *); @@ -18,55 +20,71 @@ static int dirvec_cmp(const void *d1, const void *d2) void dirvec_sort(struct dirvec *dv) { + pthread_mutex_lock(&nr_lock); qsort(dv->base, dv->nr, sizeof(struct directory *), dirvec_cmp); + pthread_mutex_unlock(&nr_lock); } 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) { + pthread_mutex_lock(&nr_lock); ++dv->nr; dv->base = xrealloc(dv->base, dv_size(dv)); dv->base[dv->nr - 1] = add; + 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, @@ -74,13 +92,17 @@ int dirvec_for_each(const struct dirvec *dv, { size_t i; + pthread_mutex_lock(&nr_lock); for (i = 0; i < dv->nr; ++i) { struct directory *dir = dv->base[i]; assert(dir); + pthread_mutex_unlock(&nr_lock); if (fn(dir, arg) < 0) return -1; + pthread_mutex_lock(&nr_lock); /* dv->nr may change in fn() */ } + pthread_mutex_unlock(&nr_lock); return 0; } -- cgit v1.2.3 From bb106c3bf8daf366a4b4ca7229010c038f89755c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 12 Oct 2008 03:35:45 -0700 Subject: directory: always maintain sorted properties vectors This allows clients to see sorted results while we're updating the DB and removes the need for us to have to sort manually. We'll have to write separate routines for managing stored playlists with songvecs eventually; but that's for another day. --- src/dirvec.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'src/dirvec.c') diff --git a/src/dirvec.c b/src/dirvec.c index 228c54be9..91b371b8a 100644 --- a/src/dirvec.c +++ b/src/dirvec.c @@ -18,13 +18,6 @@ static int dirvec_cmp(const void *d1, const void *d2) return strcmp(a->path, b->path); } -void dirvec_sort(struct dirvec *dv) -{ - pthread_mutex_lock(&nr_lock); - qsort(dv->base, dv->nr, sizeof(struct directory *), dirvec_cmp); - pthread_mutex_unlock(&nr_lock); -} - struct directory *dirvec_find(const struct dirvec *dv, const char *path) { int i; @@ -69,10 +62,14 @@ int dirvec_delete(struct dirvec *dv, struct directory *del) void dirvec_add(struct dirvec *dv, struct directory *add) { + size_t old_nr; + pthread_mutex_lock(&nr_lock); - ++dv->nr; + 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); } -- cgit v1.2.3 From c7579ca2d8422f0172537e1ca7d1bd46edfc4f9d Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 12 Oct 2008 05:08:12 -0700 Subject: update: fix multiple deletes from *vec iterators {song,dir}vec_for_each each failed to gracefully handle deleted files when iterating through. While we were thread-safe, we were not safe within the calling thread. If a callback we passed caused sv->nr to shring, our index would still increment; causing files to stay in the database. A way to test this is to remove 10 or so contiguous songs from a >10 song directory. --- src/dirvec.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/dirvec.c') diff --git a/src/dirvec.c b/src/dirvec.c index 91b371b8a..a5eb6d54e 100644 --- a/src/dirvec.c +++ b/src/dirvec.c @@ -88,16 +88,20 @@ 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; ++i) { + 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); -- cgit v1.2.3