From 06698504cafb9d3b87674c51bbd49dbbb6a54db0 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Thu, 11 Feb 2010 20:03:10 +0100 Subject: playlist thread-safety measures --- deadbeef.h | 13 +- junklib.c | 29 ---- junklib.h | 3 - main.c | 5 +- playlist.c | 346 ++++++++++++++++++++++++++++++++++++++++------ playlist.h | 51 +++++-- plugins.c | 7 +- plugins/ffap/ffap.c | 3 + plugins/gtkui/callbacks.c | 4 +- plugins/gtkui/fileman.c | 2 +- plugins/gtkui/tabs.c | 15 +- streamer.c | 14 ++ threading.c | 44 ------ threading.h | 39 ++++-- threading_pthread.c | 21 ++- 15 files changed, 442 insertions(+), 154 deletions(-) delete mode 100644 threading.c diff --git a/deadbeef.h b/deadbeef.h index d5f2f979..3c173c50 100644 --- a/deadbeef.h +++ b/deadbeef.h @@ -285,8 +285,11 @@ typedef struct { void (*plt_free) (void); void (*plt_set_curr) (int plt); int (*plt_get_curr) (void); - const char * (*plt_get_title) (int plt); - void (*plt_set_title) (int plt, const char *title); + int (*plt_get_title) (int plt, char *buffer, int bufsize); + int (*plt_set_title) (int plt, const char *title); + // playlist control + void (*pl_lock) (void); + void (*pl_unlock) (void); // playlist tracks access DB_playItem_t * (*pl_item_alloc) (void); void (*pl_item_ref) (DB_playItem_t *it); @@ -308,7 +311,7 @@ typedef struct { int (*pl_get_cursor) (int iter); void (*pl_set_selected) (DB_playItem_t *it, int sel); int (*pl_is_selected) (DB_playItem_t *it); - void (*pl_free) (void); + void (*pl_clear) (void); int (*pl_load) (const char *name); int (*pl_save) (const char *name); void (*pl_select_all) (void); @@ -351,12 +354,14 @@ typedef struct { void (*pl_search_process) (const char *text); // metainfo void (*pl_add_meta) (DB_playItem_t *it, const char *key, const char *value); - const char *(*pl_find_meta) (DB_playItem_t *song, const char *meta); + // must be used from within explicit pl_lock/unlock block + const char *(*pl_find_meta) (DB_playItem_t *it, const char *key); void (*pl_delete_all_meta) (DB_playItem_t *it); void (*pl_replace_meta) (DB_playItem_t *it, const char *key, const char *value); void (*pl_set_item_duration) (DB_playItem_t *it, float duration); float (*pl_get_item_duration) (DB_playItem_t *it); void (*pl_sort) (int iter, int id, const char *format, int ascending); + void (*pl_items_copy_junk)(DB_playItem_t *from, DB_playItem_t *first, DB_playItem_t *last); // playqueue support int (*pl_playqueue_push) (DB_playItem_t *it); void (*pl_playqueue_clear) (void); diff --git a/junklib.c b/junklib.c index 11270db4..643caecb 100644 --- a/junklib.c +++ b/junklib.c @@ -1328,32 +1328,3 @@ junk_recode (const char *in, int inlen, char *out, int outlen, const char *cs) { } } -void -junk_copy (playItem_t *from, playItem_t *first, playItem_t *last) { - const char *year = pl_find_meta (from, "year"); - const char *genre = pl_find_meta (from, "genre"); - const char *copyright = pl_find_meta (from, "copyright"); - const char *vendor = pl_find_meta (from, "vendor"); - const char *comment = pl_find_meta (from, "comment"); - playItem_t *i; - for (i = first; i; i = i->next[PL_MAIN]) { - if (year) { - pl_add_meta (i, "year", year); - } - if (genre) { - pl_add_meta (i, "genre", genre); - } - if (copyright) { - pl_add_meta (i, "copyright", copyright); - } - if (vendor) { - pl_add_meta (i, "vendor", vendor); - } - if (comment) { - pl_add_meta (i, "comment", comment); - } - if (i == last) { - break; - } - } -} diff --git a/junklib.h b/junklib.h index e02be590..8d1a40ca 100644 --- a/junklib.h +++ b/junklib.h @@ -40,7 +40,4 @@ junk_detect_charset (const char *s); void junk_recode (const char *in, int inlen, char *out, int outlen, const char *cs); -void -junk_copy (struct playItem_s *from, struct playItem_s *first, struct playItem_s *last); - #endif // __JUNKLIB_H diff --git a/main.c b/main.c index c5914531..fab717ad 100644 --- a/main.c +++ b/main.c @@ -563,7 +563,8 @@ main (int argc, char *argv[]) { } - conf_load (); // required by some plugin at startup + pl_init (); + conf_load (); // required by some plugins at startup messagepump_init (); // required to push messages while handling commandline plug_load_all (); // required to add files to playlist from commandline @@ -639,11 +640,11 @@ main (int argc, char *argv[]) { plug_unload_all (); // at this point we can simply do exit(0), but let's clean up for debugging - pl_free (); conf_free (); messagepump_free (); plt_free (); plug_free_decoder_ids (); + pl_free (); sigterm_handled = 1; fprintf (stderr, "hej-hej!\n"); return 0; diff --git a/playlist.c b/playlist.c index 3a304b7d..d51b81bc 100644 --- a/playlist.c +++ b/playlist.c @@ -40,6 +40,7 @@ #include "conf.h" #include "utf8.h" #include "common.h" +#include "threading.h" #pragma GCC optimize("O0") @@ -64,11 +65,77 @@ static playlist_t *playlists_head = NULL; static playlist_t *playlist = NULL; // current playlist static int plt_loading = 0; // disable sending event about playlist switch, config regen, etc +static uintptr_t mutex; +static uintptr_t mutex_plt; + +#define LOCK {pl_lock();} +#define UNLOCK {pl_unlock();} + +#define PLT_LOCK {plt_lock();} +#define PLT_UNLOCK {plt_unlock();} + +#define GLOBAL_LOCK {pl_global_lock();} +#define GLOBAL_UNLOCK {pl_global_unlock();} + +int +pl_init (void) { + mutex = mutex_create_recursive (); + mutex_plt = mutex_create_recursive (); +} + +void +pl_free (void) { + if (mutex) { + mutex_free (mutex); + mutex = 0; + } + if (mutex_plt) { + mutex_free (mutex_plt); + mutex_plt = 0; + } +} + +void +plt_lock (void) { + mutex_lock (mutex_plt); +} + +void +plt_unlock (void) { + mutex_unlock (mutex_plt); +} + +void +pl_lock (void) { + mutex_lock (mutex); +} + +void +pl_unlock (void) { + mutex_unlock (mutex); +} + +void +pl_global_lock (void) { + PLT_LOCK; + LOCK; +} + +void +pl_global_unlock (void) { + UNLOCK; + PLT_UNLOCK; +} + +static void +pl_item_free (playItem_t *it); + static void plt_gen_conf (void) { if (plt_loading) { return; } + PLT_LOCK; int cnt = plt_get_count (); int i; conf_remove_items ("playlist.tab."); @@ -79,10 +146,13 @@ plt_gen_conf (void) { snprintf (s, sizeof (s), "playlist.tab.%d", i); conf_set_str (s, p->title); } + PLT_UNLOCK; } -static void -pl_item_free (playItem_t *it); +playlist_t * +plt_get_curr_ptr (void) { + return playlist; +} int plt_get_count (void) { @@ -91,10 +161,14 @@ plt_get_count (void) { void plt_add (int before, const char *title) { + trace ("plt_add\n"); playlist_t *plt = malloc (sizeof (playlist_t)); memset (plt, 0, sizeof (playlist_t)); plt->title = strdup (title); + trace ("locking\n"); + PLT_LOCK; + trace ("locked\n"); playlist_t *p_before = NULL; playlist_t *p_after = playlists_head; @@ -121,11 +195,12 @@ plt_add (int before, const char *title) { plt->next = p_after; playlists_count++; - plt_gen_conf (); - if (!playlist) { playlist = plt; } + PLT_UNLOCK; + + plt_gen_conf (); if (!plt_loading) { plug_trigger_event (DB_EV_PLAYLISTSWITCH, 0); } @@ -134,11 +209,13 @@ plt_add (int before, const char *title) { void plt_remove (int plt) { int i; + PLT_LOCK; if (!plt_loading && (playlists_head && !playlists_head->next)) { trace ("warning: deleting last playlist\n"); - pl_free (); + pl_clear (); free (playlist->title); playlist->title = strdup ("Default"); + PLT_UNLOCK; plt_gen_conf (); if (!plt_loading) { plug_trigger_event (DB_EV_PLAYLISTSWITCH, 0); @@ -168,6 +245,7 @@ plt_remove (int plt) { free (p->title); free (p); playlists_count--; + PLT_UNLOCK; plt_gen_conf (); if (!plt_loading) { @@ -178,11 +256,13 @@ plt_remove (int plt) { void plt_set_curr (int plt) { int i; + PLT_LOCK; playlist_t *p = playlists_head; for (i = 0; p && i < plt; i++) { p = p->next; } if (i != plt) { + PLT_UNLOCK; trace ("plt_set_curr %d failed\n", plt); return; } @@ -192,64 +272,81 @@ plt_set_curr (int plt) { plug_trigger_event (DB_EV_PLAYLISTSWITCH, 0); } } + PLT_UNLOCK; } int plt_get_curr (void) { int i; + PLT_LOCK; playlist_t *p = playlists_head; for (i = 0; p && i < playlists_count; i++) { if (p == playlist) { + PLT_UNLOCK; return i; } p = p->next; } + PLT_UNLOCK; return -1; } -const char * -plt_get_title (int plt) { +int +plt_get_title (int plt, char *buffer, int bufsize) { int i; + PLT_LOCK; playlist_t *p = playlists_head; for (i = 0; p && i <= plt; i++) { if (i == plt) { - return p->title; + strncpy (buffer, p->title, bufsize); + buffer[bufsize-1] = 0; + PLT_UNLOCK; + return 0; } p = p->next; } - return NULL; + PLT_UNLOCK; + buffer[0] = 0; + return -1; } -void +int plt_set_title (int plt, const char *title) { int i; + PLT_LOCK; playlist_t *p = playlists_head; for (i = 0; p && i <= plt; i++) { if (i == plt) { free (p->title); p->title = strdup (title); - return; + PLT_UNLOCK; + return 0; } p = p->next; } + PLT_UNLOCK; + return -1; } -playlist_t * -plt_get_curr_ptr (void) { - return playlist; -} +//playlist_t * +//plt_get_curr_ptr (void) { +// return playlist; +//} void plt_free (void) { + PLT_LOCK; plt_loading = 1; while (playlists_head) { plt_remove (0); } plt_loading = 0; + PLT_UNLOCK; } void -pl_free (void) { +pl_clear (void) { + LOCK; while (playlist->head[PL_MAIN]) { pl_remove (playlist->head[PL_MAIN]); } @@ -259,6 +356,7 @@ pl_free (void) { if (playlist->current_row[PL_SEARCH] >= playlist->count[PL_SEARCH]) { playlist->current_row[PL_SEARCH] = playlist->count[PL_SEARCH]-1; } + UNLOCK; } static const uint8_t * @@ -427,12 +525,14 @@ pl_process_cue_track (playItem_t *after, const char *fname, playItem_t **prev, c if (date[0]) { pl_add_meta (it, "year", date); } + pl_item_unref (it); *prev = it; return it; } playItem_t * pl_insert_cue_from_buffer (playItem_t *after, playItem_t *origin, const uint8_t *buffer, int buffersize, int numsamples, int samplerate) { + LOCK; playItem_t *ins = after; trace ("pl_insert_cue_from_buffer numsamples=%d, samplerate=%d\n", numsamples, samplerate); char performer[256] = ""; @@ -489,6 +589,7 @@ pl_insert_cue_from_buffer (playItem_t *after, playItem_t *origin, const uint8_t else if (!strncmp (p, "TRACK ", 6)) { // add previous track after = pl_process_cue_track (after, origin->fname, &prev, track, index00, index01, pregap, title, performer, albumtitle, genre, date, origin->decoder_id, origin->filetype, samplerate); + track[0] = 0; title[0] = 0; pregap[0] = 0; @@ -520,9 +621,14 @@ pl_insert_cue_from_buffer (playItem_t *after, playItem_t *origin, const uint8_t after->endsample = numsamples-1; pl_set_item_duration (after, (float)(after->endsample - after->startsample + 1) / samplerate); } + // add caller ref + if (after && after != ins) { + pl_item_ref (after); + } // copy metadata from embedded tags playItem_t *first = ins ? ins->next[PL_MAIN] : playlist->head[PL_MAIN]; - junk_copy (origin, first, after); + pl_items_copy_junk (origin, first, after); + UNLOCK; return after; } @@ -586,6 +692,8 @@ pl_insert_m3u (playItem_t *after, const char *fname, int *pabort, int (*cb)(play vfs_fclose (fp); const uint8_t *p = buffer; const uint8_t *end = buffer+sz; + LOCK; + pl_item_ref (after); while (p < end) { p = pl_str_skipspaces (p, end); if (p >= end) { @@ -611,9 +719,11 @@ pl_insert_m3u (playItem_t *after, const char *fname, int *pabort, int (*cb)(play trace ("adding file %s\n", nm); playItem_t *it = pl_insert_file (after, nm, pabort, cb, user_data); if (it) { + pl_item_unref (after); after = it; } if (pabort && *pabort) { + UNLOCK; return after; } p = e; @@ -621,6 +731,7 @@ pl_insert_m3u (playItem_t *after, const char *fname, int *pabort, int (*cb)(play break; } } + UNLOCK; return after; } @@ -675,6 +786,8 @@ pl_insert_pls (playItem_t *after, const char *fname, int *pabort, int (*cb)(play char url[1024] = ""; char title[1024] = ""; char length[20] = ""; + LOCK; + pl_item_ref (after); while (p < end) { p = pl_str_skipspaces (p, end); if (p >= end) { @@ -698,6 +811,10 @@ pl_insert_pls (playItem_t *after, const char *fname, int *pabort, int (*cb)(play } } if (pabort && *pabort) { + if (after) { + pl_item_ref (after); + } + UNLOCK; return after; } url[0] = 0; @@ -774,6 +891,9 @@ pl_insert_pls (playItem_t *after, const char *fname, int *pabort, int (*cb)(play if (url[0]) { playItem_t *it = pl_insert_file (after, url, pabort, cb, user_data); if (it) { + if (after) { + pl_item_unref (after); + } after = it; pl_set_item_duration (it, atoi (length)); if (title[0]) { @@ -782,6 +902,7 @@ pl_insert_pls (playItem_t *after, const char *fname, int *pabort, int (*cb)(play } } } + UNLOCK; return after; } @@ -928,7 +1049,9 @@ pl_insert_dir (playItem_t *after, const char *dirname, int *pabort, int (*cb)(pl int pl_add_file (const char *fname, int (*cb)(playItem_t *it, void *data), void *user_data) { int abort = 0; - if (pl_insert_file (playlist->tail[PL_MAIN], fname, &abort, cb, user_data)) { + playItem_t *it = pl_insert_file (playlist->tail[PL_MAIN], fname, &abort, cb, user_data); + if (it) { + pl_item_unref (it); return 0; } return -1; @@ -937,7 +1060,9 @@ pl_add_file (const char *fname, int (*cb)(playItem_t *it, void *data), void *use int pl_add_dir (const char *dirname, int (*cb)(playItem_t *it, void *data), void *user_data) { int abort = 0; - if (pl_insert_dir (playlist->tail[PL_MAIN], dirname, &abort, cb, user_data)) { + playItem_t *it = pl_insert_dir (playlist->tail[PL_MAIN], dirname, &abort, cb, user_data); + if (it) { + pl_item_unref (it); return 0; } return -1; @@ -950,7 +1075,8 @@ pl_remove (playItem_t *it) { streamer_song_removed_notify (it); pl_playqueue_remove (it); - // remove from both lists list + // remove from both lists + LOCK; for (int iter = PL_MAIN; iter <= PL_SEARCH; iter++) { if (it->prev[iter] || it->next[iter] || playlist->head[iter] == it || playlist->tail[iter] == it) { playlist->count[iter]--; @@ -976,13 +1102,16 @@ pl_remove (playItem_t *it) { playlist->totaltime = 0; } } + UNLOCK; pl_item_unref (it); - //free (it); return 0; } int pl_getcount (int iter) { + if (!playlist) { + return 0; + } return playlist->count[iter]; } @@ -990,22 +1119,29 @@ int pl_getselcount (void) { // FIXME: slow! int cnt = 0; + LOCK; for (playItem_t *it = playlist->head[PL_MAIN]; it; it = it->next[PL_MAIN]) { if (it->selected) { cnt++; } } + UNLOCK; return cnt; } playItem_t * pl_get_for_idx_and_iter (int idx, int iter) { + LOCK; playItem_t *it = playlist->head[iter]; while (idx--) { if (!it) return NULL; it = it->next[iter]; } + if (it) { + pl_item_ref (it); + } + UNLOCK; return it; } @@ -1016,6 +1152,7 @@ pl_get_for_idx (int idx) { int pl_get_idx_of (playItem_t *it) { + LOCK; playItem_t *c = playlist->head[PL_MAIN]; int idx = 0; while (c && c != it) { @@ -1025,11 +1162,14 @@ pl_get_idx_of (playItem_t *it) { if (!c) { return -1; } + UNLOCK; return idx; } playItem_t * pl_insert_item (playItem_t *after, playItem_t *it) { + GLOBAL_LOCK; + pl_item_ref (it); if (!after) { it->next[PL_MAIN] = playlist->head[PL_MAIN]; it->prev[PL_MAIN] = NULL; @@ -1064,11 +1204,13 @@ pl_insert_item (playItem_t *after, playItem_t *it) { playlist->totaltime += it->_duration; } + GLOBAL_UNLOCK; return it; } void pl_item_copy (playItem_t *out, playItem_t *it) { + LOCK; out->fname = strdup (it->fname); out->decoder_id = it->decoder_id; out->tracknum = it->tracknum; @@ -1104,6 +1246,7 @@ pl_item_copy (playItem_t *out, playItem_t *it) { prev = m; meta = meta->next; } + UNLOCK; } playItem_t * @@ -1118,11 +1261,14 @@ pl_item_alloc (void) { void pl_item_ref (playItem_t *it) { + LOCK; it->_refc++; + UNLOCK; } static void pl_item_free (playItem_t *it) { + LOCK; if (it) { if (it->fname) { free (it->fname); @@ -1133,27 +1279,33 @@ pl_item_free (playItem_t *it) { free (m->value); free (m); } - memset (it, 0, sizeof (playItem_t)); + free (it); } + UNLOCK; } void pl_item_unref (playItem_t *it) { + LOCK; it->_refc--; if (it->_refc < 0) { - fprintf (stderr, "playlist: bad refcount on item %p\n", it); + fprintf (stderr, "\033[0;31mplaylist: bad refcount on item %p\033[37;0m\n", it); } if (it->_refc <= 0) { pl_item_free (it); } + UNLOCK; } void pl_add_meta (playItem_t *it, const char *key, const char *value) { + LOCK; // check if it's already set metaInfo_t *m = it->meta; while (m) { if (!strcasecmp (key, m->key)) { + // duplicate key + UNLOCK; return; } m = m->next; @@ -1179,6 +1331,7 @@ pl_add_meta (playItem_t *it, const char *key, const char *value) { value = str; } else { + UNLOCK; return; } } @@ -1187,10 +1340,12 @@ pl_add_meta (playItem_t *it, const char *key, const char *value) { m->value = strdup (value); m->next = it->meta; it->meta = m; + UNLOCK; } void pl_replace_meta (playItem_t *it, const char *key, const char *value) { + LOCK; // check if it's already set metaInfo_t *m = it->meta; while (m) { @@ -1202,15 +1357,18 @@ pl_replace_meta (playItem_t *it, const char *key, const char *value) { if (m) { free (m->value); m->value = strdup (value); + UNLOCK; return; } else { pl_add_meta (it, key, value); } + UNLOCK; } void pl_format_item_display_name (playItem_t *it, char *str, int len) { + LOCK; const char *artist = pl_find_meta (it, "artist"); const char *title = pl_find_meta (it, "title"); if (!artist) { @@ -1220,6 +1378,7 @@ pl_format_item_display_name (playItem_t *it, char *str, int len) { title = "Unknown title"; } snprintf (str, len, "%s - %s", artist, title); + UNLOCK; } const char * @@ -1236,6 +1395,7 @@ pl_find_meta (playItem_t *it, const char *key) { int pl_delete_selected (void) { + GLOBAL_LOCK; int i = 0; int ret = -1; playItem_t *next = NULL; @@ -1248,11 +1408,13 @@ pl_delete_selected (void) { pl_remove (it); } } + GLOBAL_UNLOCK; return ret; } void pl_crop_selected (void) { + GLOBAL_LOCK; playItem_t *next = NULL; for (playItem_t *it = playlist->head[PL_MAIN]; it; it = next) { next = it->next[PL_MAIN]; @@ -1260,6 +1422,7 @@ pl_crop_selected (void) { pl_remove (it); } } + GLOBAL_UNLOCK; } int @@ -1271,6 +1434,7 @@ pl_save (const char *fname) { if (!fp) { return -1; } + GLOBAL_LOCK; if (fwrite (magic, 1, 4, fp) != 4) { goto save_fail; } @@ -1373,9 +1537,11 @@ pl_save (const char *fname) { } } } + GLOBAL_UNLOCK; fclose (fp); return 0; save_fail: + GLOBAL_UNLOCK; fclose (fp); unlink (fname); return -1; @@ -1383,19 +1549,21 @@ save_fail: int pl_save_all (void) { - playlist_t *p = playlists_head; - int i; - int cnt = plt_get_count (); - int curr = plt_get_curr (); - int err = 0; - // make folder char path[1024]; if (snprintf (path, sizeof (path), "%s/playlists", dbconfdir) > sizeof (path)) { fprintf (stderr, "error: failed to make path string for playlists folder\n"); return -1; } + // make folder mkdir (path, 0755); + PLT_LOCK; + playlist_t *p = playlists_head; + int i; + int cnt = plt_get_count (); + int curr = plt_get_curr (); + int err = 0; + plt_loading = 1; for (i = 0; i < cnt; i++, p = p->next) { plt_set_curr (i); @@ -1411,19 +1579,21 @@ pl_save_all (void) { } plt_set_curr (curr); plt_loading = 0; + PLT_UNLOCK; return err; } int pl_load (const char *fname) { - pl_free (); - uint8_t majorver; - uint8_t minorver; - playItem_t *it = NULL; FILE *fp = fopen (fname, "rb"); if (!fp) { return -1; } + GLOBAL_LOCK; + pl_clear (); + uint8_t majorver; + uint8_t minorver; + playItem_t *it = NULL; char magic[4]; if (fread (magic, 1, 4, fp) != 4) { goto load_fail; @@ -1602,16 +1772,24 @@ pl_load (const char *fname) { } } pl_insert_item (playlist->tail[PL_MAIN], it); + pl_item_unref (it); + it = NULL; + } + GLOBAL_UNLOCK; + if (fp) { + fclose (fp); } - fclose (fp); return 0; load_fail: fprintf (stderr, "playlist load fail (%s)!\n", fname); - fclose (fp); if (it) { pl_item_unref (it); } - pl_free (); + pl_clear (); + GLOBAL_UNLOCK; + if (fp) { + fclose (fp); + } return -1; } @@ -1632,6 +1810,9 @@ pl_load_all (void) { plt_add (plt_get_count (), "Default"); return pl_load (defpl); } + trace ("pl_load_all started\n"); + GLOBAL_LOCK; + trace ("locked\n"); plt_loading = 1; while (it) { fprintf (stderr, "INFO: loading playlist %s\n", it->value); @@ -1656,19 +1837,24 @@ pl_load_all (void) { plt_loading = 0; plt_gen_conf (); plug_trigger_event (DB_EV_PLAYLISTSWITCH, 0); + GLOBAL_UNLOCK; + trace ("pl_load_all finished\n"); return err; } void pl_select_all (void) { + GLOBAL_LOCK; for (playItem_t *it = playlist->head[PL_MAIN]; it; it = it->next[PL_MAIN]) { it->selected = 1; } + GLOBAL_UNLOCK; } void pl_reshuffle (playItem_t **ppmin, playItem_t **ppmax) { + GLOBAL_LOCK; playItem_t *pmin = NULL; playItem_t *pmax = NULL; for (playItem_t *it = playlist->head[PL_MAIN]; it; it = it->next[PL_MAIN]) { @@ -1687,10 +1873,12 @@ pl_reshuffle (playItem_t **ppmin, playItem_t **ppmax) { if (ppmax) { *ppmax = pmax; } + GLOBAL_UNLOCK; } void pl_delete_all_meta (playItem_t *it) { + LOCK; while (it->meta) { metaInfo_t *m = it->meta; it->meta = m->next; @@ -1698,11 +1886,12 @@ pl_delete_all_meta (playItem_t *it) { free (m); } it->meta = NULL; + UNLOCK; } void pl_set_item_duration (playItem_t *it, float duration) { -// if (pl_get_idx_of (it) != -1) { + GLOBAL_LOCK; if (it->in_playlist) { if (it->_duration > 0) { playlist->totaltime -= it->_duration; @@ -1715,11 +1904,15 @@ pl_set_item_duration (playItem_t *it, float duration) { } } it->_duration = duration; + GLOBAL_UNLOCK; } float pl_get_item_duration (playItem_t *it) { - return it->_duration; + LOCK; + float d = it->_duration; + UNLOCK; + return d; } int @@ -1728,6 +1921,7 @@ pl_format_item_queue (playItem_t *it, char *s, int size) { if (!playqueue_count) { return 0; } + LOCK; int init = 1; int initsize = size; int len; @@ -1755,6 +1949,7 @@ pl_format_item_queue (playItem_t *it, char *s, int size) { s += len; size -= len; } + UNLOCK; return initsize-size; } @@ -1777,6 +1972,7 @@ pl_format_time (float t, char *dur, int size) { } } +// following helpers must be called from within pl_lock/unlock block static const char * pl_get_meta_cached (playItem_t *it, const char *meta, const char *ret, const char *def) { if (!ret) { @@ -1824,6 +2020,7 @@ pl_format_title (playItem_t *it, int idx, char *s, int size, int id, const char const char *copyright = NULL; const char *filename = NULL; + LOCK; if (id != -1) { const char *text = NULL; switch (id) { @@ -1835,6 +2032,7 @@ pl_format_title (playItem_t *it, int idx, char *s, int size, int id, const char text = fno; break; case DB_COLUMN_PLAYING: + UNLOCK; return pl_format_item_queue (it, s, size); case DB_COLUMN_ARTIST_ALBUM: { @@ -1875,11 +2073,13 @@ pl_format_title (playItem_t *it, int idx, char *s, int size, int id, const char } if (text) { strncpy (s, text, size); + UNLOCK; return strlen (s); } else { s[0] = 0; } + UNLOCK; return 0; } int n = size-1; @@ -1960,6 +2160,7 @@ pl_format_title (playItem_t *it, int idx, char *s, int size, int id, const char } *s = 0; + UNLOCK; return size - n - 1; } @@ -1968,6 +2169,7 @@ pl_sort (int iter, int id, const char *format, int ascending) { if (id == DB_COLUMN_FILENUMBER) { return; } + GLOBAL_LOCK; int sorted = 0; do { sorted = 1; @@ -2027,14 +2229,17 @@ pl_sort (int iter, int id, const char *format, int ascending) { } } } while (!sorted); + GLOBAL_UNLOCK; } void pl_reset_cursor (void) { int i; + PLT_LOCK; for (i = 0; i < PL_MAX_ITERATORS; i++) { playlist->current_row[i] = -1; } + PLT_UNLOCK; } float @@ -2044,7 +2249,9 @@ pl_get_totaltime (void) { void pl_set_selected (playItem_t *it, int sel) { + LOCK; it->selected = sel; + UNLOCK; } int @@ -2075,14 +2282,16 @@ pl_get_prev (playItem_t *it, int iter) { int pl_get_cursor (int iter) { if (!playlist) { - return -1; // FIXME: that's a workaround for crashes in plugins + return -1; } return playlist->current_row[iter]; } void pl_set_cursor (int iter, int cursor) { + PLT_LOCK; playlist->current_row[iter] = cursor; + PLT_UNLOCK; } // this function must move items in playlist @@ -2091,6 +2300,7 @@ pl_set_cursor (int iter, int cursor) { void pl_move_items (int iter, playItem_t *drop_before, uint32_t *indexes, int count) { // unlink items from playlist, and link together + GLOBAL_LOCK; playItem_t *head = NULL; playItem_t *tail = NULL; int processed = 0; @@ -2146,10 +2356,12 @@ pl_move_items (int iter, playItem_t *drop_before, uint32_t *indexes, int count) else { playlist->tail[iter] = tail; } + GLOBAL_UNLOCK; } void pl_search_reset (void) { + GLOBAL_LOCK; while (playlist->head[PL_SEARCH]) { playItem_t *next = playlist->head[PL_SEARCH]->next[PL_SEARCH]; playlist->head[PL_SEARCH]->selected = 0; @@ -2159,16 +2371,17 @@ pl_search_reset (void) { } playlist->tail[PL_SEARCH] = NULL; playlist->count[PL_SEARCH] = 0; + GLOBAL_UNLOCK; } void pl_search_process (const char *text) { pl_search_reset (); + GLOBAL_LOCK; for (playItem_t *it = playlist->head[PL_MAIN]; it; it = it->next[PL_MAIN]) { it->selected = 0; if (*text) { for (metaInfo_t *m = it->meta; m; m = m->next) { - // if (strcasestr (m->value, text)) { if (strcasecmp (m->key, "cuesheet") && utfcasestr (m->value, text)) { //fprintf (stderr, "%s -> %s match (%s.%s)\n", text, m->value, it->fname, m->key); // add to list @@ -2185,8 +2398,9 @@ pl_search_process (const char *text) { break; } } - } } + } + GLOBAL_LOCK; } int @@ -2195,13 +2409,17 @@ pl_playqueue_push (playItem_t *it) { trace ("playqueue is full\n"); return -1; } + LOCK; playqueue[playqueue_count++] = it; + UNLOCK; return 0; } void pl_playqueue_clear (void) { + LOCK; playqueue_count = 0; + UNLOCK; } void @@ -2209,16 +2427,20 @@ pl_playqueue_pop (void) { if (!playqueue_count) { return; } + LOCK; if (playqueue_count == 1) { playqueue_count = 0; + UNLOCK; return; } memmove (&playqueue[0], &playqueue[1], (playqueue_count-1) * sizeof (playItem_t*)); playqueue_count--; + UNLOCK; } void pl_playqueue_remove (playItem_t *it) { + LOCK; for (;;) { int i; for (i = 0; i < playqueue_count; i++) { @@ -2234,23 +2456,31 @@ pl_playqueue_remove (playItem_t *it) { break; } } + UNLOCK; } int pl_playqueue_test (playItem_t *it) { + LOCK; for (int i = 0; i < playqueue_count; i++) { if (playqueue[i] == it) { + UNLOCK; return i; } } + UNLOCK; return -1; } playItem_t * pl_playqueue_getnext (void) { + LOCK; if (playqueue_count > 0) { - return playqueue[0]; + playItem_t *val = playqueue[0]; + UNLOCK; + return val; } + UNLOCK; return NULL; } @@ -2258,3 +2488,35 @@ int pl_playqueue_getcount (void) { return playqueue_count; } + +void +pl_items_copy_junk (playItem_t *from, playItem_t *first, playItem_t *last) { + LOCK; + const char *year = pl_find_meta (from, "year"); + const char *genre = pl_find_meta (from, "genre"); + const char *copyright = pl_find_meta (from, "copyright"); + const char *vendor = pl_find_meta (from, "vendor"); + const char *comment = pl_find_meta (from, "comment"); + playItem_t *i; + for (i = first; i; i = i->next[PL_MAIN]) { + if (year) { + pl_add_meta (i, "year", year); + } + if (genre) { + pl_add_meta (i, "genre", genre); + } + if (copyright) { + pl_add_meta (i, "copyright", copyright); + } + if (vendor) { + pl_add_meta (i, "vendor", vendor); + } + if (comment) { + pl_add_meta (i, "comment", comment); + } + if (i == last) { + break; + } + } + UNLOCK; +} diff --git a/playlist.h b/playlist.h index 5c4210fb..df695662 100644 --- a/playlist.h +++ b/playlist.h @@ -65,7 +65,38 @@ typedef struct playlist_s { struct playlist_s *next; } playlist_t; +// global playlist control functions +int +pl_init (void); + +void +pl_free (void); + +void +pl_lock (void); + +void +pl_unlock (void); + +void +plt_lock (void); + +void +plt_unlock (void); + +void +pl_global_lock (void); + +void +pl_global_unlock (void); + + // playlist management functions + +// it is highly recommended to access that from inside plt_lock/unlock block +playlist_t * +plt_get_curr_ptr (void); + int plt_get_count (void); @@ -84,16 +115,16 @@ plt_set_curr (int plt); int plt_get_curr (void); -const char * -plt_get_title (int plt); +int +plt_get_title (int plt, char *buffer, int bufsize); -void +int plt_set_title (int plt, const char *title); -playlist_t * -plt_get_curr_ptr (void); - // playlist access functions +void +pl_clear (void); + int pl_add_dir (const char *dirname, int (*cb)(playItem_t *it, void *data), void *user_data); @@ -127,9 +158,6 @@ pl_item_unref (playItem_t *it); void pl_item_copy (playItem_t *out, playItem_t *it); -void -pl_free (void); - int pl_getcount (int iter); @@ -154,6 +182,8 @@ pl_insert_cue (playItem_t *after, playItem_t *origin, int numsamples, int sample void pl_add_meta (playItem_t *it, const char *key, const char *value); +// must be used in explicit pl_lock/unlock block +// that makes it possible to avoid copying metadata on every access const char * pl_find_meta (playItem_t *it, const char *key); @@ -267,4 +297,7 @@ pl_playqueue_getnext (void); int pl_playqueue_getcount (void); +void +pl_items_copy_junk (struct playItem_s *from, struct playItem_s *first, struct playItem_s *last); + #endif // __PLAYLIST_H diff --git a/plugins.c b/plugins.c index 56ecb0a1..81a9141c 100644 --- a/plugins.c +++ b/plugins.c @@ -111,6 +111,8 @@ static DB_functions_t deadbeef_api = { .plt_get_title = plt_get_title, .plt_set_title = plt_set_title, // playlist access + .pl_lock = pl_lock, + .pl_unlock = pl_unlock, .pl_item_alloc = (DB_playItem_t* (*)(void))pl_item_alloc, .pl_item_ref = (void (*)(DB_playItem_t *))pl_item_ref, .pl_item_unref = (void (*)(DB_playItem_t *))pl_item_unref, @@ -126,6 +128,7 @@ static DB_functions_t deadbeef_api = { .pl_set_item_duration = (void (*) (DB_playItem_t *it, float duration))pl_set_item_duration, .pl_get_item_duration = (float (*) (DB_playItem_t *it))pl_get_item_duration, .pl_sort = pl_sort, + .pl_items_copy_junk = (void (*)(DB_playItem_t *from, DB_playItem_t *first, DB_playItem_t *last))pl_items_copy_junk, .pl_get_totaltime = pl_get_totaltime, .pl_getcount = pl_getcount, .pl_getcurrent = (DB_playItem_t *(*)(void))streamer_get_playing_track, @@ -134,7 +137,7 @@ static DB_functions_t deadbeef_api = { .pl_get_cursor = pl_get_cursor, .pl_set_selected = (void (*) (DB_playItem_t *, int))pl_set_selected, .pl_is_selected = (int (*) (DB_playItem_t *))pl_is_selected, - .pl_free = pl_free, + .pl_clear = pl_clear, .pl_load = pl_load, .pl_save = pl_save, .pl_select_all = pl_select_all, @@ -157,6 +160,7 @@ static DB_functions_t deadbeef_api = { // cuesheet support .pl_insert_cue_from_buffer = (DB_playItem_t *(*) (DB_playItem_t *after, DB_playItem_t *origin, const uint8_t *buffer, int buffersize, int numsamples, int samplerate))pl_insert_cue_from_buffer, .pl_insert_cue = (DB_playItem_t *(*)(DB_playItem_t *after, DB_playItem_t *origin, int numsamples, int samplerate))pl_insert_cue, + // playqueue support .pl_playqueue_push = (int (*) (DB_playItem_t *))pl_playqueue_push, .pl_playqueue_clear = pl_playqueue_clear, .pl_playqueue_pop = pl_playqueue_pop, @@ -173,7 +177,6 @@ static DB_functions_t deadbeef_api = { .junk_read_id3v2 = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_read_id3v2, .junk_read_ape = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_read_ape, .junk_get_leading_size = junk_get_leading_size, - .junk_copy = (void (*)(DB_playItem_t *from, DB_playItem_t *first, DB_playItem_t *last))junk_copy, .junk_detect_charset = junk_detect_charset, .junk_recode = junk_recode, // vfs diff --git a/plugins/ffap/ffap.c b/plugins/ffap/ffap.c index 23daa170..d8acaff5 100644 --- a/plugins/ffap/ffap.c +++ b/plugins/ffap/ffap.c @@ -1726,14 +1726,17 @@ ffap_insert (DB_playItem_t *after, const char *fname) { } // embedded cue + deadbeef->pl_lock (); const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet"); if (cuesheet) { cue = deadbeef->pl_insert_cue_from_buffer (after, it, cuesheet, strlen (cuesheet), ape_ctx.totalsamples, ape_ctx.samplerate); if (cue) { deadbeef->pl_item_unref (it); + deadbeef->pl_unlock (); return cue; } } + deadbeef->pl_unlock (); deadbeef->pl_add_meta (it, "title", NULL); after = deadbeef->pl_insert_item (after, it); diff --git a/plugins/gtkui/callbacks.c b/plugins/gtkui/callbacks.c index 1af091db..4982dccc 100644 --- a/plugins/gtkui/callbacks.c +++ b/plugins/gtkui/callbacks.c @@ -402,7 +402,7 @@ on_open_activate (GtkMenuItem *menuitem, } if (response == GTK_RESPONSE_OK) { - deadbeef->pl_free (); + deadbeef->pl_clear (); GSList *lst = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (dlg)); gtk_widget_destroy (dlg); if (lst) { @@ -501,7 +501,7 @@ void on_clear1_activate (GtkMenuItem *menuitem, gpointer user_data) { - deadbeef->pl_free (); + deadbeef->pl_clear (); gtkplaylist_t *ps = &main_playlist; GtkWidget *widget = ps->playlist; gtkpl_setup_scrollbar (ps); diff --git a/plugins/gtkui/fileman.c b/plugins/gtkui/fileman.c index d181fa1f..ecda32ae 100644 --- a/plugins/gtkui/fileman.c +++ b/plugins/gtkui/fileman.c @@ -36,7 +36,7 @@ open_files_worker (void *data) { void gtkui_open_files (struct _GSList *lst) { - deadbeef->pl_free (); + deadbeef->pl_clear (); deadbeef->thread_start (open_files_worker, lst); } diff --git a/plugins/gtkui/tabs.c b/plugins/gtkui/tabs.c index 6d472e3b..cf891980 100644 --- a/plugins/gtkui/tabs.c +++ b/plugins/gtkui/tabs.c @@ -51,7 +51,8 @@ tabbar_draw (GtkWidget *widget) { int widths[cnt]; int fullwidth = 0; for (idx = 0; idx < cnt; idx++) { - const char *title = deadbeef->plt_get_title (idx); + char title[100]; + deadbeef->plt_get_title (idx, title, sizeof (title)); widths[idx] = 0; int h = 0; draw_get_text_extents (title, strlen (title), &widths[idx], &h); @@ -74,7 +75,8 @@ tabbar_draw (GtkWidget *widget) { GdkColor *gdkfg = &widget->style->fg[0]; float fg[3] = {(float)gdkfg->red/0xffff, (float)gdkfg->green/0xffff, (float)gdkfg->blue/0xffff}; draw_set_fg_color (fg); - const char *tab_title = deadbeef->plt_get_title (idx); + char tab_title[100]; + deadbeef->plt_get_title (idx, tab_title, sizeof (tab_title)); draw_text (x + margin_size + 5, h/2-draw_get_font_size()/2, w - margin_size, 0, tab_title); } x += margin_size; @@ -98,7 +100,8 @@ tabbar_draw (GtkWidget *widget) { GdkColor *gdkfg = &widget->style->fg[0]; float fg[3] = {(float)gdkfg->red/0xffff, (float)gdkfg->green/0xffff, (float)gdkfg->blue/0xffff}; draw_set_fg_color (fg); - const char *tab_title = deadbeef->plt_get_title (idx); + char tab_title[100]; + deadbeef->plt_get_title (idx, tab_title, sizeof (tab_title)); draw_text (x + margin_size + 5, h/2-draw_get_font_size()/2, w - margin_size, 0, tab_title); } if (need_draw_moving) { @@ -136,7 +139,8 @@ get_tab_under_cursor (int x) { int fw = 0; int tab_selected = deadbeef->plt_get_curr (); for (idx = 0; idx < cnt; idx++) { - const char *title = deadbeef->plt_get_title (idx); + char title[100]; + deadbeef->plt_get_title (idx, title, sizeof (title)); int w = 0; int h = 0; draw_get_text_extents (title, strlen (title), &w, &h); @@ -253,7 +257,8 @@ on_add_new_playlist1_activate (GtkMenuItem *menuitem, snprintf (name, sizeof (name), "New Playlist (%d)", idx); } for (i = 0; i < cnt; i++) { - const char *t = deadbeef->plt_get_title (i); + char t[100]; + deadbeef->plt_get_title (i, t, sizeof (t)); if (!strcasecmp (t, name)) { break; } diff --git a/streamer.c b/streamer.c index 5c10bd54..ec28c88c 100644 --- a/streamer.c +++ b/streamer.c @@ -102,18 +102,21 @@ streamer_get_playing_track (void) { int streamer_move_to_nextsong (int reason) { + pl_global_lock (); playlist_t *plt = plt_get_curr_ptr (); playItem_t *it = pl_playqueue_getnext (); if (it) { pl_playqueue_pop (); int r = pl_get_idx_of (it); streamer_set_nextsong (r, 1); + pl_global_unlock (); return 0; } playItem_t *curr = playlist_track; if (!plt->head[PL_MAIN]) { streamer_set_nextsong (-2, 1); + pl_global_unlock (); return 0; } int pl_order = conf_get_int ("playback.order", 0); @@ -138,10 +141,12 @@ streamer_move_to_nextsong (int reason) { } } if (!it) { + pl_global_unlock (); return -1; } int r = pl_get_idx_of (it); streamer_set_nextsong (r, 1); + pl_global_unlock (); return 0; } else { @@ -149,6 +154,7 @@ streamer_move_to_nextsong (int reason) { if (reason == 0 && pl_loop_mode == PLAYBACK_MODE_LOOP_SINGLE) { // song finished, loop mode is "loop 1 track" int r = pl_get_idx_of (curr); streamer_set_nextsong (r, 1); + pl_global_unlock (); return 0; } // find minimal notplayed above current @@ -171,10 +177,12 @@ streamer_move_to_nextsong (int reason) { } } if (!it) { + pl_global_unlock (); return -1; } int r = pl_get_idx_of (it); streamer_set_nextsong (r, 1); + pl_global_unlock (); return 0; } } @@ -184,6 +192,7 @@ streamer_move_to_nextsong (int reason) { if (reason == 0 && pl_loop_mode == PLAYBACK_MODE_LOOP_SINGLE) { // loop same track int r = pl_get_idx_of (curr); streamer_set_nextsong (r, 1); + pl_global_unlock (); return 0; } it = curr->next[PL_MAIN]; @@ -195,24 +204,29 @@ streamer_move_to_nextsong (int reason) { } else { streamer_set_nextsong (-2, 1); + pl_global_unlock (); return 0; } } if (!it) { + pl_global_unlock (); return -1; } int r = pl_get_idx_of (it); streamer_set_nextsong (r, 1); + pl_global_unlock (); return 0; } else if (pl_order == PLAYBACK_ORDER_RANDOM) { // random if (reason == 0 && pl_loop_mode == PLAYBACK_MODE_LOOP_SINGLE && curr) { int r = pl_get_idx_of (curr); streamer_set_nextsong (r, 1); + pl_global_unlock (); return 0; } return streamer_move_to_randomsong (); } + pl_global_unlock (); return -1; } diff --git a/threading.c b/threading.c deleted file mode 100644 index c9651349..00000000 --- a/threading.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ -#include -#include -#include "threading.h" - -void thread_start (void (*fn)(uintptr_t ctx), uintptr_t ctx) { - if (!SDL_CreateThread ((int (*)(void*))fn, (void*)ctx)) { - printf ("SDL_CreateThread failed!\n"); - } -} -uintptr_t mutex_create (void) { - SDL_mutex *mtx = SDL_CreateMutex (); - if (!mtx) { - printf ("SDL_CreateMutex failed!\n"); - } - return (uintptr_t)mtx; -} -void mutex_free (uintptr_t mtx) { - SDL_mutexP ((SDL_mutex*)mtx); // grant that no thread does processing now - SDL_DestroyMutex ((SDL_mutex*)mtx); -} -int mutex_lock (uintptr_t mtx) { - return SDL_mutexP ((SDL_mutex*)mtx); -} -int mutex_unlock (uintptr_t mtx) { - return SDL_mutexV ((SDL_mutex*)mtx); -} - diff --git a/threading.h b/threading.h index 66b91571..f8767360 100644 --- a/threading.h +++ b/threading.h @@ -26,16 +26,35 @@ thread_start (void (*fn)(void *ctx), void *ctx); int thread_join (intptr_t tid); -uintptr_t mutex_create (void); -void mutex_free (uintptr_t mtx); -int mutex_lock (uintptr_t mtx); -int mutex_unlock (uintptr_t mtx); - -uintptr_t cond_create (void); -void cond_free (uintptr_t cond); -int cond_wait (uintptr_t cond, uintptr_t mutex); -int cond_signal (uintptr_t cond); -int cond_broadcast (uintptr_t cond); +uintptr_t +mutex_create (void); + +uintptr_t +mutex_create_recursive (void); + +void +mutex_free (uintptr_t mtx); + +int +mutex_lock (uintptr_t mtx); + +int +mutex_unlock (uintptr_t mtx); + +uintptr_t +cond_create (void); + +void +cond_free (uintptr_t cond); + +int +cond_wait (uintptr_t cond, uintptr_t mutex); + +int +cond_signal (uintptr_t cond); + +int +cond_broadcast (uintptr_t cond); #endif diff --git a/threading_pthread.c b/threading_pthread.c index 25bc0026..c3e4e8fb 100644 --- a/threading_pthread.c +++ b/threading_pthread.c @@ -60,11 +60,30 @@ thread_join (intptr_t tid) { uintptr_t mutex_create (void) { pthread_mutex_t *mtx = malloc (sizeof (pthread_mutex_t)); - int err = pthread_mutex_init (mtx, NULL); + pthread_mutexattr_t attr = {0}; + pthread_mutexattr_init (&attr); + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL); + int err = pthread_mutex_init (mtx, &attr); if (err != 0) { fprintf (stderr, "pthread_mutex_init failed: %s\n", strerror (err)); return 0; } + pthread_mutexattr_destroy (&attr); + return (uintptr_t)mtx; +} + +uintptr_t +mutex_create_recursive (void) { + pthread_mutex_t *mtx = malloc (sizeof (pthread_mutex_t)); + pthread_mutexattr_t attr = {0}; + pthread_mutexattr_init (&attr); + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); + int err = pthread_mutex_init (mtx, &attr); + if (err != 0) { + fprintf (stderr, "pthread_mutex_init failed: %s\n", strerror (err)); + return 0; + } + pthread_mutexattr_destroy (&attr); return (uintptr_t)mtx; } -- cgit v1.2.3