diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2023-01-21 17:48:11 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2023-01-23 12:24:12 +0000 |
commit | 65b3ff81c5fba2e72aa21c7c80dc7133b51e042b (patch) | |
tree | 7be4749f8ffa087d5e7393c8ba106ea4bf49cccf /apps | |
parent | ce52d0c870f715ee979a1bf5e51857c200543d9d (diff) | |
download | rockbox-65b3ff81c5fba2e72aa21c7c80dc7133b51e042b.tar.gz rockbox-65b3ff81c5fba2e72aa21c7c80dc7133b51e042b.zip |
playlist: Fix dircache scan thread deadlocks
Fix deadlocks in the dircache scan thread caused by incorrect
lock ordering. Mutating operations need to stop the thread to
prevent it from accessing invalid data; this must always be
done before taking the playlist lock to avoid deadlocking the
scan thread.
Change-Id: If719a8b28ed0b0b3eac068073581e606c4a5f58a
Diffstat (limited to 'apps')
-rw-r--r-- | apps/playlist.c | 467 |
1 files changed, 246 insertions, 221 deletions
diff --git a/apps/playlist.c b/apps/playlist.c index 7b7b91c22f..bf65bb8656 100644 --- a/apps/playlist.c +++ b/apps/playlist.c | |||
@@ -67,6 +67,7 @@ | |||
67 | flushed to disk when required. | 67 | flushed to disk when required. |
68 | */ | 68 | */ |
69 | 69 | ||
70 | //#define LOGF_ENABLE | ||
70 | #include <stdio.h> | 71 | #include <stdio.h> |
71 | #include <stdlib.h> | 72 | #include <stdlib.h> |
72 | #include <ctype.h> | 73 | #include <ctype.h> |
@@ -106,11 +107,9 @@ | |||
106 | #ifdef HAVE_DIRCACHE | 107 | #ifdef HAVE_DIRCACHE |
107 | #include "dircache.h" | 108 | #include "dircache.h" |
108 | #endif | 109 | #endif |
110 | #include "logf.h" | ||
109 | 111 | ||
110 | #if 0//def ROCKBOX_HAS_LOGDISKF | 112 | #if 0//def ROCKBOX_HAS_LOGDISKF |
111 | #warning LOGF enabled | ||
112 | #define LOGF_ENABLE | ||
113 | #include "logf.h" | ||
114 | #undef DEBUGF | 113 | #undef DEBUGF |
115 | #undef ERRORF | 114 | #undef ERRORF |
116 | #undef WARNF | 115 | #undef WARNF |
@@ -119,9 +118,6 @@ | |||
119 | #define ERRORF DEBUGF | 118 | #define ERRORF DEBUGF |
120 | #define WARNF DEBUGF | 119 | #define WARNF DEBUGF |
121 | #define NOTEF DEBUGF | 120 | #define NOTEF DEBUGF |
122 | //ERRORF | ||
123 | //WARNF | ||
124 | //NOTEF | ||
125 | #endif | 121 | #endif |
126 | 122 | ||
127 | /* default load buffer size (should be at least 1 KiB) */ | 123 | /* default load buffer size (should be at least 1 KiB) */ |
@@ -190,12 +186,11 @@ static void dc_init_filerefs(struct playlist_info *playlist, | |||
190 | } | 186 | } |
191 | 187 | ||
192 | #ifdef HAVE_DIRCACHE | 188 | #ifdef HAVE_DIRCACHE |
193 | #define PLAYLIST_LOAD_POINTERS 1 | 189 | #define PLAYLIST_DC_SCAN_START 1 |
194 | #define PLAYLIST_CLEAN_POINTERS 2 | 190 | #define PLAYLIST_DC_SCAN_STOP 2 |
195 | static bool dc_has_dirty_pointers = false; | ||
196 | 191 | ||
197 | static struct event_queue playlist_queue SHAREDBSS_ATTR; | 192 | static struct event_queue playlist_queue; |
198 | static struct queue_sender_list playlist_queue_sender_list SHAREDBSS_ATTR; | 193 | static struct queue_sender_list playlist_queue_sender_list; |
199 | static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)]; | 194 | static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)]; |
200 | static const char dc_thread_playlist_name[] = "playlist cachectrl"; | 195 | static const char dc_thread_playlist_name[] = "playlist cachectrl"; |
201 | #endif | 196 | #endif |
@@ -232,22 +227,20 @@ static void notify_buffer_full(void) | |||
232 | splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL)); | 227 | splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL)); |
233 | } | 228 | } |
234 | 229 | ||
235 | static void dc_load_playlist_pointers(void) | 230 | static void dc_thread_start(struct playlist_info *playlist, bool is_dirty) |
236 | { | 231 | { |
237 | #ifdef HAVE_DIRCACHE | 232 | #ifdef HAVE_DIRCACHE |
238 | queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0); | 233 | if (playlist == ¤t_playlist) |
234 | queue_post(&playlist_queue, PLAYLIST_DC_SCAN_START, is_dirty); | ||
239 | #endif | 235 | #endif |
240 | /* No-Op for non dircache targets */ | ||
241 | } | 236 | } |
242 | 237 | ||
243 | static void dc_discard_playlist_pointers(void) | 238 | static void dc_thread_stop(struct playlist_info *playlist) |
244 | { | 239 | { |
245 | #ifdef HAVE_DIRCACHE | 240 | #ifdef HAVE_DIRCACHE |
246 | /* dump any pointers currently waiting */ | 241 | if (playlist == ¤t_playlist) |
247 | if (dc_has_dirty_pointers) | 242 | queue_send(&playlist_queue, PLAYLIST_DC_SCAN_STOP, 0); |
248 | queue_send(&playlist_queue, PLAYLIST_CLEAN_POINTERS, 0); | ||
249 | #endif | 243 | #endif |
250 | /* No-Op for non dircache targets */ | ||
251 | } | 244 | } |
252 | 245 | ||
253 | static void close_playlist_control_file(struct playlist_info *playlist) | 246 | static void close_playlist_control_file(struct playlist_info *playlist) |
@@ -547,8 +540,6 @@ static void update_playlist_filename_unlocked(struct playlist_info* playlist, | |||
547 | */ | 540 | */ |
548 | static void empty_playlist_unlocked(struct playlist_info* playlist, bool resume) | 541 | static void empty_playlist_unlocked(struct playlist_info* playlist, bool resume) |
549 | { | 542 | { |
550 | dc_discard_playlist_pointers(); | ||
551 | |||
552 | if(playlist->fd >= 0) /* If there is an already open playlist, close it. */ | 543 | if(playlist->fd >= 0) /* If there is an already open playlist, close it. */ |
553 | { | 544 | { |
554 | close(playlist->fd); | 545 | close(playlist->fd); |
@@ -1556,8 +1547,8 @@ static int directory_search_callback(char* filename, void* context) | |||
1556 | /* | 1547 | /* |
1557 | * remove track at specified position | 1548 | * remove track at specified position |
1558 | */ | 1549 | */ |
1559 | static int remove_track_from_playlist(struct playlist_info* playlist, | 1550 | static int remove_track_unlocked(struct playlist_info* playlist, |
1560 | int position, bool write) | 1551 | int position, bool write) |
1561 | { | 1552 | { |
1562 | int i; | 1553 | int i; |
1563 | int result = 0; | 1554 | int result = 0; |
@@ -1566,8 +1557,6 @@ static int remove_track_from_playlist(struct playlist_info* playlist, | |||
1566 | if (playlist->amount <= 0) | 1557 | if (playlist->amount <= 0) |
1567 | return -1; | 1558 | return -1; |
1568 | 1559 | ||
1569 | playlist_mutex_lock(&(playlist->mutex)); | ||
1570 | |||
1571 | inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK; | 1560 | inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK; |
1572 | 1561 | ||
1573 | #ifdef HAVE_DIRCACHE | 1562 | #ifdef HAVE_DIRCACHE |
@@ -1613,7 +1602,6 @@ static int remove_track_from_playlist(struct playlist_info* playlist, | |||
1613 | sync_control(playlist, false); | 1602 | sync_control(playlist, false); |
1614 | } | 1603 | } |
1615 | 1604 | ||
1616 | playlist_mutex_unlock(&(playlist->mutex)); | ||
1617 | return result; | 1605 | return result; |
1618 | } | 1606 | } |
1619 | 1607 | ||
@@ -1642,18 +1630,14 @@ static void find_and_set_playlist_index_unlocked(struct playlist_info* playlist, | |||
1642 | * randomly rearrange the array of indices for the playlist. If start_current | 1630 | * randomly rearrange the array of indices for the playlist. If start_current |
1643 | * is true then update the index to the new index of the current playing track | 1631 | * is true then update the index to the new index of the current playing track |
1644 | */ | 1632 | */ |
1645 | static int randomise_playlist(struct playlist_info* playlist, | 1633 | static int randomise_playlist_unlocked(struct playlist_info* playlist, |
1646 | unsigned int seed, bool start_current, | 1634 | unsigned int seed, bool start_current, |
1647 | bool write) | 1635 | bool write) |
1648 | { | 1636 | { |
1649 | int count; | 1637 | int count; |
1650 | int candidate; | 1638 | int candidate; |
1651 | unsigned int current = playlist->indices[playlist->index]; | 1639 | unsigned int current = playlist->indices[playlist->index]; |
1652 | 1640 | ||
1653 | playlist_mutex_lock(&(playlist->mutex)); | ||
1654 | |||
1655 | dc_discard_playlist_pointers(); | ||
1656 | |||
1657 | /* seed 0 is used to identify sorted playlist for resume purposes */ | 1641 | /* seed 0 is used to identify sorted playlist for resume purposes */ |
1658 | if (seed == 0) | 1642 | if (seed == 0) |
1659 | seed = 1; | 1643 | seed = 1; |
@@ -1698,8 +1682,6 @@ static int randomise_playlist(struct playlist_info* playlist, | |||
1698 | playlist->first_index, NULL, NULL, NULL); | 1682 | playlist->first_index, NULL, NULL, NULL); |
1699 | } | 1683 | } |
1700 | 1684 | ||
1701 | playlist_mutex_unlock(&(playlist->mutex)); | ||
1702 | |||
1703 | return 0; | 1685 | return 0; |
1704 | } | 1686 | } |
1705 | 1687 | ||
@@ -1740,8 +1722,6 @@ static int sort_playlist_unlocked(struct playlist_info* playlist, | |||
1740 | { | 1722 | { |
1741 | unsigned int current = playlist->indices[playlist->index]; | 1723 | unsigned int current = playlist->indices[playlist->index]; |
1742 | 1724 | ||
1743 | dc_discard_playlist_pointers(); | ||
1744 | |||
1745 | if (playlist->amount > 0) | 1725 | if (playlist->amount > 0) |
1746 | qsort((void*)playlist->indices, playlist->amount, | 1726 | qsort((void*)playlist->indices, playlist->amount, |
1747 | sizeof(playlist->indices[0]), sort_compare_fn); | 1727 | sizeof(playlist->indices[0]), sort_compare_fn); |
@@ -1751,7 +1731,6 @@ static int sort_playlist_unlocked(struct playlist_info* playlist, | |||
1751 | * sort two arrays at once :/ | 1731 | * sort two arrays at once :/ |
1752 | * FIXME: Please implement a better way to do this. */ | 1732 | * FIXME: Please implement a better way to do this. */ |
1753 | dc_init_filerefs(playlist, 0, playlist->max_playlist_size); | 1733 | dc_init_filerefs(playlist, 0, playlist->max_playlist_size); |
1754 | dc_load_playlist_pointers(); | ||
1755 | #endif | 1734 | #endif |
1756 | 1735 | ||
1757 | if (start_current) | 1736 | if (start_current) |
@@ -1917,55 +1896,81 @@ static void dc_thread_playlist(void) | |||
1917 | int seek; | 1896 | int seek; |
1918 | bool control_file; | 1897 | bool control_file; |
1919 | 1898 | ||
1920 | int sleep_time = 5; | 1899 | /* Thread starts out stopped */ |
1921 | 1900 | long sleep_time = TIMEOUT_BLOCK; | |
1922 | #ifdef HAVE_DISK_STORAGE | 1901 | int stop_count = 1; |
1923 | if (global_settings.disk_spindown > 1 && | 1902 | bool is_dirty = false; |
1924 | global_settings.disk_spindown <= 5) | ||
1925 | sleep_time = global_settings.disk_spindown - 1; | ||
1926 | #endif | ||
1927 | 1903 | ||
1928 | while (1) | 1904 | while (1) |
1929 | { | 1905 | { |
1930 | queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time); | 1906 | queue_wait_w_tmo(&playlist_queue, &ev, sleep_time); |
1931 | 1907 | ||
1932 | switch (ev.id) | 1908 | switch (ev.id) |
1933 | { | 1909 | { |
1934 | case PLAYLIST_CLEAN_POINTERS: | 1910 | case PLAYLIST_DC_SCAN_START: |
1935 | dc_has_dirty_pointers = false; | 1911 | if (ev.data) |
1936 | queue_reply(&playlist_queue, 0); | 1912 | is_dirty = true; |
1913 | |||
1914 | stop_count--; | ||
1915 | if (is_dirty && stop_count == 0) | ||
1916 | { | ||
1917 | /* Start the background scanning after either the disk | ||
1918 | * spindown timeout or 5s, whichever is less */ | ||
1919 | sleep_time = 5 * HZ; | ||
1920 | #ifdef HAVE_DISK_STORAGE | ||
1921 | if (global_settings.disk_spindown > 1 && | ||
1922 | global_settings.disk_spindown <= 5) | ||
1923 | sleep_time = (global_settings.disk_spindown - 1) * HZ; | ||
1924 | #endif | ||
1925 | } | ||
1937 | break; | 1926 | break; |
1938 | 1927 | ||
1939 | case PLAYLIST_LOAD_POINTERS: | 1928 | case PLAYLIST_DC_SCAN_STOP: |
1940 | dc_has_dirty_pointers = true; | 1929 | stop_count++; |
1941 | break ; | 1930 | sleep_time = TIMEOUT_BLOCK; |
1931 | queue_reply(&playlist_queue, 0); | ||
1932 | break; | ||
1942 | 1933 | ||
1943 | /* Start the background scanning after either the disk spindown | ||
1944 | timeout or 5s, whichever is less */ | ||
1945 | case SYS_TIMEOUT: | 1934 | case SYS_TIMEOUT: |
1946 | { | 1935 | { |
1936 | /* Nothing to do if there are no dcfrefs or tracks */ | ||
1947 | if (!playlist->dcfrefs_handle || playlist->amount <= 0) | 1937 | if (!playlist->dcfrefs_handle || playlist->amount <= 0) |
1948 | break ; | 1938 | { |
1949 | 1939 | is_dirty = false; | |
1950 | /* Check if previously loaded pointers are intact. */ | 1940 | sleep_time = TIMEOUT_BLOCK; |
1951 | if (!dc_has_dirty_pointers) | 1941 | logf("%s: nothing to scan", __func__); |
1952 | break ; | 1942 | break; |
1943 | } | ||
1953 | 1944 | ||
1945 | /* Retry at a later time if the dircache is not ready */ | ||
1954 | struct dircache_info info; | 1946 | struct dircache_info info; |
1955 | dircache_get_info(&info); | 1947 | dircache_get_info(&info); |
1956 | |||
1957 | if (info.status != DIRCACHE_READY) | 1948 | if (info.status != DIRCACHE_READY) |
1958 | break ; | 1949 | { |
1950 | logf("%s: dircache not ready", __func__); | ||
1951 | break; | ||
1952 | } | ||
1953 | |||
1954 | logf("%s: scan start", __func__); | ||
1955 | #ifdef LOGF_ENABLE | ||
1956 | long scan_start_tick = current_tick; | ||
1957 | #endif | ||
1959 | 1958 | ||
1960 | trigger_cpu_boost(); | 1959 | trigger_cpu_boost(); |
1961 | dcfrefs = core_get_data_pinned(playlist->dcfrefs_handle); | 1960 | dcfrefs = core_get_data_pinned(playlist->dcfrefs_handle); |
1962 | 1961 | ||
1963 | for (index = 0; index < playlist->amount | 1962 | for (index = 0; index < playlist->amount; index++) |
1964 | && queue_empty(&playlist_queue); index++) | ||
1965 | { | 1963 | { |
1966 | /* Process only pointers that are superficially stale. */ | 1964 | /* Process only pointers that are superficially stale. */ |
1967 | if (dircache_search(DCS_FILEREF, &dcfrefs[index], NULL) > 0) | 1965 | if (dircache_search(DCS_FILEREF, &dcfrefs[index], NULL) > 0) |
1968 | continue ; | 1966 | continue; |
1967 | |||
1968 | /* Bail out if a command needs servicing. */ | ||
1969 | if (!queue_empty(&playlist_queue)) | ||
1970 | { | ||
1971 | logf("%s: scan interrupted", __func__); | ||
1972 | break; | ||
1973 | } | ||
1969 | 1974 | ||
1970 | control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK; | 1975 | control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK; |
1971 | seek = playlist->indices[index] & PLAYLIST_SEEK_MASK; | 1976 | seek = playlist->indices[index] & PLAYLIST_SEEK_MASK; |
@@ -1973,9 +1978,7 @@ static void dc_thread_playlist(void) | |||
1973 | /* Load the filename from playlist file. */ | 1978 | /* Load the filename from playlist file. */ |
1974 | if (get_track_filename(playlist, index, seek, | 1979 | if (get_track_filename(playlist, index, seek, |
1975 | control_file, tmp, sizeof(tmp)) < 0) | 1980 | control_file, tmp, sizeof(tmp)) < 0) |
1976 | { | 1981 | break; |
1977 | break ; | ||
1978 | } | ||
1979 | 1982 | ||
1980 | /* Obtain the dircache file entry cookie. */ | 1983 | /* Obtain the dircache file entry cookie. */ |
1981 | dircache_search(DCS_CACHED_PATH | DCS_UPDATE_FILEREF, | 1984 | dircache_search(DCS_CACHED_PATH | DCS_UPDATE_FILEREF, |
@@ -1985,19 +1988,26 @@ static void dc_thread_playlist(void) | |||
1985 | yield(); | 1988 | yield(); |
1986 | } | 1989 | } |
1987 | 1990 | ||
1991 | /* If we indexed the whole playlist without being interrupted | ||
1992 | * then there are no dirty references; go to sleep. */ | ||
1993 | if (index == playlist->amount) | ||
1994 | { | ||
1995 | is_dirty = false; | ||
1996 | sleep_time = TIMEOUT_BLOCK; | ||
1997 | logf("%s: scan complete", __func__); | ||
1998 | } | ||
1999 | |||
1988 | core_put_data_pinned(dcfrefs); | 2000 | core_put_data_pinned(dcfrefs); |
1989 | cancel_cpu_boost(); | 2001 | cancel_cpu_boost(); |
1990 | 2002 | ||
1991 | if (index == playlist->amount) | 2003 | logf("%s: %ld ticks", __func__, current_tick - scan_start_tick); |
1992 | dc_has_dirty_pointers = false; | 2004 | break; |
1993 | |||
1994 | break ; | ||
1995 | } | 2005 | } |
1996 | 2006 | ||
1997 | case SYS_USB_CONNECTED: | 2007 | case SYS_USB_CONNECTED: |
1998 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | 2008 | usb_acknowledge(SYS_USB_CONNECTED_ACK); |
1999 | usb_wait_for_disconnect(&playlist_queue); | 2009 | usb_wait_for_disconnect(&playlist_queue); |
2000 | break ; | 2010 | break; |
2001 | } | 2011 | } |
2002 | } | 2012 | } |
2003 | } | 2013 | } |
@@ -2098,6 +2108,8 @@ void playlist_init(void) | |||
2098 | queue_init(&playlist_queue, true); | 2108 | queue_init(&playlist_queue, true); |
2099 | queue_enable_queue_send(&playlist_queue, | 2109 | queue_enable_queue_send(&playlist_queue, |
2100 | &playlist_queue_sender_list, playlist_thread_id); | 2110 | &playlist_queue_sender_list, playlist_thread_id); |
2111 | |||
2112 | dc_thread_start(¤t_playlist, false); | ||
2101 | #endif /* HAVE_DIRCACHE */ | 2113 | #endif /* HAVE_DIRCACHE */ |
2102 | } | 2114 | } |
2103 | 2115 | ||
@@ -2183,65 +2195,56 @@ int playlist_amount(void) | |||
2183 | * playlist off disk for viewing/editing. The index_buffer is used to store | 2195 | * playlist off disk for viewing/editing. The index_buffer is used to store |
2184 | * playlist indices (required for and only used if !current playlist). The | 2196 | * playlist indices (required for and only used if !current playlist). The |
2185 | * temp_buffer (if not NULL) is used as a scratchpad when loading indices. | 2197 | * temp_buffer (if not NULL) is used as a scratchpad when loading indices. |
2198 | * | ||
2199 | * XXX: This is really only usable by the playlist viewer. Never pass | ||
2200 | * playlist == NULL, that cannot and will not work. | ||
2186 | */ | 2201 | */ |
2187 | int playlist_create_ex(struct playlist_info* playlist, | 2202 | int playlist_create_ex(struct playlist_info* playlist, |
2188 | const char* dir, const char* file, | 2203 | const char* dir, const char* file, |
2189 | void* index_buffer, int index_buffer_size, | 2204 | void* index_buffer, int index_buffer_size, |
2190 | void* temp_buffer, int temp_buffer_size) | 2205 | void* temp_buffer, int temp_buffer_size) |
2191 | { | 2206 | { |
2192 | if (!playlist) | 2207 | /* Initialize playlist structure */ |
2193 | { | 2208 | int r = rand() % 10; |
2194 | playlist = ¤t_playlist; | ||
2195 | playlist_mutex_lock(&(playlist->mutex)); | ||
2196 | playlist->last_shuffled_start = playlist->amount; | ||
2197 | playlist_mutex_unlock(&(playlist->mutex)); | ||
2198 | } | ||
2199 | else | ||
2200 | { | ||
2201 | /* Initialize playlist structure */ | ||
2202 | int r = rand() % 10; | ||
2203 | 2209 | ||
2204 | /* Use random name for control file */ | 2210 | /* Use random name for control file */ |
2205 | snprintf(playlist->control_filename, sizeof(playlist->control_filename), | 2211 | snprintf(playlist->control_filename, sizeof(playlist->control_filename), |
2206 | "%s.%d", PLAYLIST_CONTROL_FILE, r); | 2212 | "%s.%d", PLAYLIST_CONTROL_FILE, r); |
2207 | playlist->fd = -1; | 2213 | playlist->fd = -1; |
2208 | playlist->control_fd = -1; | 2214 | playlist->control_fd = -1; |
2209 | 2215 | ||
2210 | if (index_buffer) | 2216 | if (index_buffer) |
2211 | { | 2217 | { |
2212 | int num_indices = index_buffer_size / | 2218 | int num_indices = index_buffer_size / |
2213 | playlist_get_required_bufsz(playlist, false, 1); | 2219 | playlist_get_required_bufsz(playlist, false, 1); |
2214 | 2220 | ||
2215 | if (num_indices > global_settings.max_files_in_playlist) | 2221 | if (num_indices > global_settings.max_files_in_playlist) |
2216 | num_indices = global_settings.max_files_in_playlist; | 2222 | num_indices = global_settings.max_files_in_playlist; |
2217 | 2223 | ||
2218 | playlist->max_playlist_size = num_indices; | 2224 | playlist->max_playlist_size = num_indices; |
2219 | playlist->indices = index_buffer; | 2225 | playlist->indices = index_buffer; |
2220 | #ifdef HAVE_DIRCACHE | 2226 | #ifdef HAVE_DIRCACHE |
2221 | playlist->dcfrefs_handle = 0; | 2227 | playlist->dcfrefs_handle = 0; |
2222 | #endif | 2228 | #endif |
2223 | } | 2229 | } |
2224 | else | 2230 | else |
2225 | { | 2231 | { |
2226 | /* FIXME not sure if it's safe to share index buffers */ | 2232 | /* FIXME not sure if it's safe to share index buffers */ |
2227 | playlist->max_playlist_size = current_playlist.max_playlist_size; | 2233 | playlist->max_playlist_size = current_playlist.max_playlist_size; |
2228 | playlist->indices = current_playlist.indices; | 2234 | playlist->indices = current_playlist.indices; |
2229 | #ifdef HAVE_DIRCACHE | 2235 | #ifdef HAVE_DIRCACHE |
2230 | playlist->dcfrefs_handle = 0; | 2236 | playlist->dcfrefs_handle = 0; |
2231 | #endif | 2237 | #endif |
2232 | } | ||
2233 | |||
2234 | chunk_alloc_free(&playlist->name_chunk_buffer); | ||
2235 | } | 2238 | } |
2236 | 2239 | ||
2240 | chunk_alloc_free(&playlist->name_chunk_buffer); | ||
2241 | |||
2237 | new_playlist_unlocked(playlist, dir, file); | 2242 | new_playlist_unlocked(playlist, dir, file); |
2238 | 2243 | ||
2244 | /* load the playlist file */ | ||
2239 | if (file) | 2245 | if (file) |
2240 | { | ||
2241 | /* load the playlist file */ | ||
2242 | add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size); | 2246 | add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size); |
2243 | dc_load_playlist_pointers(); | 2247 | |
2244 | } | ||
2245 | return 0; | 2248 | return 0; |
2246 | } | 2249 | } |
2247 | 2250 | ||
@@ -2251,6 +2254,10 @@ int playlist_create_ex(struct playlist_info* playlist, | |||
2251 | int playlist_create(const char *dir, const char *file) | 2254 | int playlist_create(const char *dir, const char *file) |
2252 | { | 2255 | { |
2253 | struct playlist_info* playlist = ¤t_playlist; | 2256 | struct playlist_info* playlist = ¤t_playlist; |
2257 | int status = 0; | ||
2258 | |||
2259 | dc_thread_stop(playlist); | ||
2260 | playlist_mutex_lock(&playlist->mutex); | ||
2254 | 2261 | ||
2255 | new_playlist_unlocked(playlist, dir, file); | 2262 | new_playlist_unlocked(playlist, dir, file); |
2256 | 2263 | ||
@@ -2266,18 +2273,20 @@ int playlist_create(const char *dir, const char *file) | |||
2266 | buflen = ALIGN_DOWN(buflen, 512); /* to avoid partial sector I/O */ | 2273 | buflen = ALIGN_DOWN(buflen, 512); /* to avoid partial sector I/O */ |
2267 | /* load the playlist file */ | 2274 | /* load the playlist file */ |
2268 | add_indices_to_playlist(playlist, buf, buflen); | 2275 | add_indices_to_playlist(playlist, buf, buflen); |
2269 | dc_load_playlist_pointers(); | ||
2270 | core_free(handle); | 2276 | core_free(handle); |
2271 | } | 2277 | } |
2272 | else | 2278 | else |
2273 | { | 2279 | { |
2274 | /* should not happen -- happens if plugin takes audio buffer */ | 2280 | /* should not happen -- happens if plugin takes audio buffer */ |
2275 | splashf(HZ * 2, "%s(): OOM", __func__); | 2281 | splashf(HZ * 2, "%s(): OOM", __func__); |
2276 | return -1; | 2282 | status = -1; |
2277 | } | 2283 | } |
2278 | } | 2284 | } |
2279 | 2285 | ||
2280 | return 0; | 2286 | playlist_mutex_unlock(&playlist->mutex); |
2287 | dc_thread_start(playlist, true); | ||
2288 | |||
2289 | return status; | ||
2281 | } | 2290 | } |
2282 | 2291 | ||
2283 | /* Returns false if 'steps' is out of bounds, else true */ | 2292 | /* Returns false if 'steps' is out of bounds, else true */ |
@@ -2331,18 +2340,24 @@ int playlist_delete(struct playlist_info* playlist, int index) | |||
2331 | if (!playlist) | 2340 | if (!playlist) |
2332 | playlist = ¤t_playlist; | 2341 | playlist = ¤t_playlist; |
2333 | 2342 | ||
2343 | dc_thread_stop(playlist); | ||
2344 | playlist_mutex_lock(&playlist->mutex); | ||
2345 | |||
2334 | if (check_control(playlist) < 0) | 2346 | if (check_control(playlist) < 0) |
2335 | { | 2347 | { |
2336 | notify_control_access_error(); | 2348 | notify_control_access_error(); |
2337 | return -1; | 2349 | result = -1; |
2350 | goto out; | ||
2338 | } | 2351 | } |
2339 | 2352 | ||
2340 | dc_discard_playlist_pointers(); | ||
2341 | |||
2342 | if (index == PLAYLIST_DELETE_CURRENT) | 2353 | if (index == PLAYLIST_DELETE_CURRENT) |
2343 | index = playlist->index; | 2354 | index = playlist->index; |
2344 | 2355 | ||
2345 | result = remove_track_from_playlist(playlist, index, true); | 2356 | result = remove_track_unlocked(playlist, index, true); |
2357 | |||
2358 | out: | ||
2359 | playlist_mutex_unlock(&playlist->mutex); | ||
2360 | dc_thread_start(playlist, false); | ||
2346 | 2361 | ||
2347 | if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) && | 2362 | if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) && |
2348 | playlist->started) | 2363 | playlist->started) |
@@ -2607,10 +2622,14 @@ int playlist_insert_directory(struct playlist_info* playlist, | |||
2607 | if (!playlist) | 2622 | if (!playlist) |
2608 | playlist = ¤t_playlist; | 2623 | playlist = ¤t_playlist; |
2609 | 2624 | ||
2625 | dc_thread_stop(playlist); | ||
2626 | playlist_mutex_lock(&playlist->mutex); | ||
2627 | |||
2610 | if (check_control(playlist) < 0) | 2628 | if (check_control(playlist) < 0) |
2611 | { | 2629 | { |
2612 | notify_control_access_error(); | 2630 | notify_control_access_error(); |
2613 | return -1; | 2631 | result = -1; |
2632 | goto out; | ||
2614 | } | 2633 | } |
2615 | 2634 | ||
2616 | if (position == PLAYLIST_REPLACE) | 2635 | if (position == PLAYLIST_REPLACE) |
@@ -2618,11 +2637,12 @@ int playlist_insert_directory(struct playlist_info* playlist, | |||
2618 | if (playlist_remove_all_tracks(playlist) == 0) | 2637 | if (playlist_remove_all_tracks(playlist) == 0) |
2619 | position = PLAYLIST_INSERT_LAST; | 2638 | position = PLAYLIST_INSERT_LAST; |
2620 | else | 2639 | else |
2621 | return -1; | 2640 | { |
2641 | result = -1; | ||
2642 | goto out; | ||
2643 | } | ||
2622 | } | 2644 | } |
2623 | 2645 | ||
2624 | dc_discard_playlist_pointers(); | ||
2625 | |||
2626 | if (queue) | 2646 | if (queue) |
2627 | count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT); | 2647 | count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT); |
2628 | else | 2648 | else |
@@ -2646,11 +2666,13 @@ int playlist_insert_directory(struct playlist_info* playlist, | |||
2646 | 2666 | ||
2647 | display_playlist_count(context.count, count_str, true); | 2667 | display_playlist_count(context.count, count_str, true); |
2648 | 2668 | ||
2669 | out: | ||
2670 | playlist_mutex_unlock(&playlist->mutex); | ||
2671 | dc_thread_start(playlist, true); | ||
2672 | |||
2649 | if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started) | 2673 | if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started) |
2650 | audio_flush_and_reload_tracks(); | 2674 | audio_flush_and_reload_tracks(); |
2651 | 2675 | ||
2652 | dc_load_playlist_pointers(); | ||
2653 | |||
2654 | return result; | 2676 | return result; |
2655 | } | 2677 | } |
2656 | 2678 | ||
@@ -2673,10 +2695,9 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam | |||
2673 | if (!playlist) | 2695 | if (!playlist) |
2674 | playlist = ¤t_playlist; | 2696 | playlist = ¤t_playlist; |
2675 | 2697 | ||
2698 | dc_thread_stop(playlist); | ||
2676 | playlist_mutex_lock(&(playlist->mutex)); | 2699 | playlist_mutex_lock(&(playlist->mutex)); |
2677 | 2700 | ||
2678 | dc_discard_playlist_pointers(); | ||
2679 | |||
2680 | cpu_boost(true); | 2701 | cpu_boost(true); |
2681 | 2702 | ||
2682 | if (check_control(playlist) < 0) | 2703 | if (check_control(playlist) < 0) |
@@ -2780,13 +2801,13 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam | |||
2780 | if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started) | 2801 | if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started) |
2781 | audio_flush_and_reload_tracks(); | 2802 | audio_flush_and_reload_tracks(); |
2782 | 2803 | ||
2783 | dc_load_playlist_pointers(); | ||
2784 | result = 0; | 2804 | result = 0; |
2785 | 2805 | ||
2786 | out: | 2806 | out: |
2787 | cpu_boost(false); | 2807 | cpu_boost(false); |
2788 | 2808 | ||
2789 | playlist_mutex_unlock(&(playlist->mutex)); | 2809 | playlist_mutex_unlock(&(playlist->mutex)); |
2810 | dc_thread_start(playlist, true); | ||
2790 | 2811 | ||
2791 | return result; | 2812 | return result; |
2792 | } | 2813 | } |
@@ -2803,10 +2824,9 @@ int playlist_insert_track(struct playlist_info* playlist, const char *filename, | |||
2803 | if (!playlist) | 2824 | if (!playlist) |
2804 | playlist = ¤t_playlist; | 2825 | playlist = ¤t_playlist; |
2805 | 2826 | ||
2827 | dc_thread_stop(playlist); | ||
2806 | playlist_mutex_lock(&(playlist->mutex)); | 2828 | playlist_mutex_lock(&(playlist->mutex)); |
2807 | 2829 | ||
2808 | dc_discard_playlist_pointers(); | ||
2809 | |||
2810 | if (check_control(playlist) < 0) | 2830 | if (check_control(playlist) < 0) |
2811 | { | 2831 | { |
2812 | notify_control_access_error(); | 2832 | notify_control_access_error(); |
@@ -2820,11 +2840,10 @@ int playlist_insert_track(struct playlist_info* playlist, const char *filename, | |||
2820 | * bunch of files from tagcache, syncing after every file wouldn't be | 2840 | * bunch of files from tagcache, syncing after every file wouldn't be |
2821 | * a good thing to do. */ | 2841 | * a good thing to do. */ |
2822 | if (sync && result >= 0) | 2842 | if (sync && result >= 0) |
2823 | { | ||
2824 | playlist_sync(playlist); | 2843 | playlist_sync(playlist); |
2825 | } | ||
2826 | 2844 | ||
2827 | playlist_mutex_unlock(&(playlist->mutex)); | 2845 | playlist_mutex_unlock(&(playlist->mutex)); |
2846 | dc_thread_start(playlist, true); | ||
2828 | 2847 | ||
2829 | return result; | 2848 | return result; |
2830 | } | 2849 | } |
@@ -2867,12 +2886,13 @@ int playlist_move(struct playlist_info* playlist, int index, int new_index) | |||
2867 | if (!playlist) | 2886 | if (!playlist) |
2868 | playlist = ¤t_playlist; | 2887 | playlist = ¤t_playlist; |
2869 | 2888 | ||
2889 | dc_thread_stop(playlist); | ||
2870 | playlist_mutex_lock(&(playlist->mutex)); | 2890 | playlist_mutex_lock(&(playlist->mutex)); |
2871 | 2891 | ||
2872 | if (check_control(playlist) < 0) | 2892 | if (check_control(playlist) < 0) |
2873 | { | 2893 | { |
2874 | notify_control_access_error(); | 2894 | notify_control_access_error(); |
2875 | goto out;; | 2895 | goto out; |
2876 | } | 2896 | } |
2877 | 2897 | ||
2878 | if (index == new_index) | 2898 | if (index == new_index) |
@@ -2919,8 +2939,6 @@ int playlist_move(struct playlist_info* playlist, int index, int new_index) | |||
2919 | goto out; | 2939 | goto out; |
2920 | } | 2940 | } |
2921 | 2941 | ||
2922 | dc_discard_playlist_pointers(); | ||
2923 | |||
2924 | /* We want to insert the track at the position that was specified by | 2942 | /* We want to insert the track at the position that was specified by |
2925 | new_index. This may be different then new_index because of the | 2943 | new_index. This may be different then new_index because of the |
2926 | shifting that will occur after the delete. | 2944 | shifting that will occur after the delete. |
@@ -2929,68 +2947,59 @@ int playlist_move(struct playlist_info* playlist, int index, int new_index) | |||
2929 | r = rotate_index(playlist, new_index); | 2947 | r = rotate_index(playlist, new_index); |
2930 | 2948 | ||
2931 | /* Delete track from original position */ | 2949 | /* Delete track from original position */ |
2932 | result = remove_track_from_playlist(playlist, index, true); | 2950 | result = remove_track_unlocked(playlist, index, true); |
2951 | if (result == -1) | ||
2952 | goto out; | ||
2933 | 2953 | ||
2934 | if (result != -1) | 2954 | if (r == 0)/* First index */ |
2935 | { | 2955 | { |
2936 | if (r == 0)/* First index */ | 2956 | new_index = PLAYLIST_PREPEND; |
2937 | { | 2957 | } |
2938 | new_index = PLAYLIST_PREPEND; | 2958 | else if (r == playlist->amount) |
2939 | } | 2959 | { |
2940 | else if (r == playlist->amount) | 2960 | /* Append */ |
2941 | { | 2961 | new_index = PLAYLIST_INSERT_LAST; |
2942 | /* Append */ | 2962 | } |
2943 | new_index = PLAYLIST_INSERT_LAST; | 2963 | else /* Calculate index of desired position */ |
2944 | } | 2964 | { |
2945 | else /* Calculate index of desired position */ | 2965 | new_index = (r+playlist->first_index)%playlist->amount; |
2946 | { | 2966 | } |
2947 | new_index = (r+playlist->first_index)%playlist->amount; | ||
2948 | } | ||
2949 | |||
2950 | result = add_track_to_playlist_unlocked(playlist, filename, | ||
2951 | new_index, queue, -1); | ||
2952 | 2967 | ||
2953 | if (result != -1) | 2968 | result = add_track_to_playlist_unlocked(playlist, filename, |
2954 | { | 2969 | new_index, queue, -1); |
2955 | if (current) | 2970 | if (result == -1) |
2956 | { | 2971 | goto out; |
2957 | /* Moved the current track */ | ||
2958 | switch (new_index) | ||
2959 | { | ||
2960 | case PLAYLIST_PREPEND: | ||
2961 | playlist->index = playlist->first_index; | ||
2962 | break; | ||
2963 | case PLAYLIST_INSERT_LAST: | ||
2964 | playlist->index = playlist->first_index - 1; | ||
2965 | if (playlist->index < 0) | ||
2966 | playlist->index += playlist->amount; | ||
2967 | break; | ||
2968 | default: | ||
2969 | playlist->index = new_index; | ||
2970 | break; | ||
2971 | } | ||
2972 | } | ||
2973 | else if ((displace_current) && (new_index != PLAYLIST_PREPEND)) | ||
2974 | { | ||
2975 | /* make the index point to the currently playing track */ | ||
2976 | playlist->index++; | ||
2977 | } | ||
2978 | 2972 | ||
2979 | playlist_mutex_unlock(&(playlist->mutex)); | 2973 | if (current) |
2980 | if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started) | 2974 | { |
2981 | audio_flush_and_reload_tracks(); | 2975 | /* Moved the current track */ |
2982 | } | 2976 | switch (new_index) |
2983 | else | ||
2984 | { | 2977 | { |
2985 | playlist_mutex_unlock(&(playlist->mutex)); | 2978 | case PLAYLIST_PREPEND: |
2979 | playlist->index = playlist->first_index; | ||
2980 | break; | ||
2981 | case PLAYLIST_INSERT_LAST: | ||
2982 | playlist->index = playlist->first_index - 1; | ||
2983 | if (playlist->index < 0) | ||
2984 | playlist->index += playlist->amount; | ||
2985 | break; | ||
2986 | default: | ||
2987 | playlist->index = new_index; | ||
2988 | break; | ||
2986 | } | 2989 | } |
2987 | } | 2990 | } |
2988 | 2991 | else if ((displace_current) && (new_index != PLAYLIST_PREPEND)) | |
2989 | dc_load_playlist_pointers(); | 2992 | { |
2990 | return result; | 2993 | /* make the index point to the currently playing track */ |
2994 | playlist->index++; | ||
2995 | } | ||
2991 | 2996 | ||
2992 | out: | 2997 | out: |
2993 | playlist_mutex_unlock(&(playlist->mutex)); | 2998 | playlist_mutex_unlock(&(playlist->mutex)); |
2999 | dc_thread_start(playlist, true); | ||
3000 | |||
3001 | if (result != -1 && playlist->started && (audio_status() & AUDIO_STATUS_PLAY)) | ||
3002 | audio_flush_and_reload_tracks(); | ||
2994 | 3003 | ||
2995 | return result; | 3004 | return result; |
2996 | } | 3005 | } |
@@ -3024,7 +3033,8 @@ int playlist_next(int steps) | |||
3024 | { | 3033 | { |
3025 | struct playlist_info* playlist = ¤t_playlist; | 3034 | struct playlist_info* playlist = ¤t_playlist; |
3026 | 3035 | ||
3027 | playlist_mutex_lock(&(playlist->mutex)); | 3036 | dc_thread_stop(playlist); |
3037 | playlist_mutex_lock(&playlist->mutex); | ||
3028 | 3038 | ||
3029 | int index; | 3039 | int index; |
3030 | 3040 | ||
@@ -3036,8 +3046,6 @@ int playlist_next(int steps) | |||
3036 | { | 3046 | { |
3037 | int i, j; | 3047 | int i, j; |
3038 | 3048 | ||
3039 | dc_discard_playlist_pointers(); | ||
3040 | |||
3041 | /* We need to delete all the queued songs */ | 3049 | /* We need to delete all the queued songs */ |
3042 | for (i=0, j=steps; i<j; i++) | 3050 | for (i=0, j=steps; i<j; i++) |
3043 | { | 3051 | { |
@@ -3045,7 +3053,7 @@ int playlist_next(int steps) | |||
3045 | 3053 | ||
3046 | if (index >= 0 && playlist->indices[index] & PLAYLIST_QUEUE_MASK) | 3054 | if (index >= 0 && playlist->indices[index] & PLAYLIST_QUEUE_MASK) |
3047 | { | 3055 | { |
3048 | remove_track_from_playlist(playlist, index, true); | 3056 | remove_track_unlocked(playlist, index, true); |
3049 | steps--; /* one less track */ | 3057 | steps--; /* one less track */ |
3050 | } | 3058 | } |
3051 | } | 3059 | } |
@@ -3062,7 +3070,7 @@ int playlist_next(int steps) | |||
3062 | /* Repeat shuffle mode. Re-shuffle playlist and resume play */ | 3070 | /* Repeat shuffle mode. Re-shuffle playlist and resume play */ |
3063 | playlist->first_index = 0; | 3071 | playlist->first_index = 0; |
3064 | sort_playlist_unlocked(playlist, false, false); | 3072 | sort_playlist_unlocked(playlist, false, false); |
3065 | randomise_playlist(playlist, current_tick, false, true); | 3073 | randomise_playlist_unlocked(playlist, current_tick, false, true); |
3066 | 3074 | ||
3067 | playlist->started = true; | 3075 | playlist->started = true; |
3068 | playlist->index = 0; | 3076 | playlist->index = 0; |
@@ -3071,9 +3079,7 @@ int playlist_next(int steps) | |||
3071 | else if (playlist->in_ram && global_settings.next_folder) | 3079 | else if (playlist->in_ram && global_settings.next_folder) |
3072 | { | 3080 | { |
3073 | /* we switch playlists here */ | 3081 | /* we switch playlists here */ |
3074 | playlist_mutex_unlock(&(playlist->mutex)); | ||
3075 | index = create_and_play_dir(steps, true); | 3082 | index = create_and_play_dir(steps, true); |
3076 | playlist_mutex_lock(&(playlist->mutex)); | ||
3077 | if (index >= 0) | 3083 | if (index >= 0) |
3078 | { | 3084 | { |
3079 | playlist->index = index; | 3085 | playlist->index = index; |
@@ -3110,7 +3116,8 @@ int playlist_next(int steps) | |||
3110 | } | 3116 | } |
3111 | 3117 | ||
3112 | out: | 3118 | out: |
3113 | playlist_mutex_unlock(&(playlist->mutex)); | 3119 | playlist_mutex_unlock(&playlist->mutex); |
3120 | dc_thread_start(playlist, true); | ||
3114 | 3121 | ||
3115 | return index; | 3122 | return index; |
3116 | } | 3123 | } |
@@ -3179,7 +3186,11 @@ const char* playlist_peek(int steps, char* buf, size_t buf_size) | |||
3179 | return temp_ptr; | 3186 | return temp_ptr; |
3180 | } | 3187 | } |
3181 | 3188 | ||
3182 | /* shuffle currently playing playlist */ | 3189 | /* |
3190 | * shuffle currently playing playlist | ||
3191 | * | ||
3192 | * TODO: Merge this with playlist_shuffle()? | ||
3193 | */ | ||
3183 | int playlist_randomise(struct playlist_info* playlist, unsigned int seed, | 3194 | int playlist_randomise(struct playlist_info* playlist, unsigned int seed, |
3184 | bool start_current) | 3195 | bool start_current) |
3185 | { | 3196 | { |
@@ -3188,12 +3199,12 @@ int playlist_randomise(struct playlist_info* playlist, unsigned int seed, | |||
3188 | if (!playlist) | 3199 | if (!playlist) |
3189 | playlist = ¤t_playlist; | 3200 | playlist = ¤t_playlist; |
3190 | 3201 | ||
3202 | dc_thread_stop(playlist); | ||
3191 | playlist_mutex_lock(&(playlist->mutex)); | 3203 | playlist_mutex_lock(&(playlist->mutex)); |
3192 | 3204 | ||
3193 | check_control(playlist); | 3205 | check_control(playlist); |
3194 | 3206 | ||
3195 | result = randomise_playlist(playlist, seed, start_current, true); | 3207 | result = randomise_playlist_unlocked(playlist, seed, start_current, true); |
3196 | |||
3197 | if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) && | 3208 | if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) && |
3198 | playlist->started) | 3209 | playlist->started) |
3199 | { | 3210 | { |
@@ -3201,6 +3212,7 @@ int playlist_randomise(struct playlist_info* playlist, unsigned int seed, | |||
3201 | } | 3212 | } |
3202 | 3213 | ||
3203 | playlist_mutex_unlock(&(playlist->mutex)); | 3214 | playlist_mutex_unlock(&(playlist->mutex)); |
3215 | dc_thread_start(playlist, true); | ||
3204 | 3216 | ||
3205 | return result; | 3217 | return result; |
3206 | } | 3218 | } |
@@ -3208,6 +3220,8 @@ int playlist_randomise(struct playlist_info* playlist, unsigned int seed, | |||
3208 | /* | 3220 | /* |
3209 | * Removes all tracks, from the playlist, leaving the presently playing | 3221 | * Removes all tracks, from the playlist, leaving the presently playing |
3210 | * track queued. | 3222 | * track queued. |
3223 | * | ||
3224 | * TODO: This is insanely slow for huge playlists. Must fix. | ||
3211 | */ | 3225 | */ |
3212 | int playlist_remove_all_tracks(struct playlist_info *playlist) | 3226 | int playlist_remove_all_tracks(struct playlist_info *playlist) |
3213 | { | 3227 | { |
@@ -3216,20 +3230,25 @@ int playlist_remove_all_tracks(struct playlist_info *playlist) | |||
3216 | if (playlist == NULL) | 3230 | if (playlist == NULL) |
3217 | playlist = ¤t_playlist; | 3231 | playlist = ¤t_playlist; |
3218 | 3232 | ||
3219 | dc_discard_playlist_pointers(); | 3233 | dc_thread_stop(playlist); |
3234 | playlist_mutex_lock(&playlist->mutex); | ||
3220 | 3235 | ||
3221 | while (playlist->index > 0) | 3236 | while (playlist->index > 0) |
3222 | if ((result = remove_track_from_playlist(playlist, 0, true)) < 0) | 3237 | if ((result = remove_track_unlocked(playlist, 0, true)) < 0) |
3223 | return result; | 3238 | goto out; |
3224 | 3239 | ||
3225 | while (playlist->amount > 1) | 3240 | while (playlist->amount > 1) |
3226 | if ((result = remove_track_from_playlist(playlist, 1, true)) < 0) | 3241 | if ((result = remove_track_unlocked(playlist, 1, true)) < 0) |
3227 | return result; | 3242 | goto out; |
3228 | 3243 | ||
3229 | if (playlist->amount == 1) { | 3244 | if (playlist->amount == 1) { |
3230 | playlist->indices[0] |= PLAYLIST_QUEUED; | 3245 | playlist->indices[0] |= PLAYLIST_QUEUED; |
3231 | } | 3246 | } |
3232 | 3247 | ||
3248 | out: | ||
3249 | playlist_mutex_unlock(&playlist->mutex); | ||
3250 | dc_thread_start(playlist, false); | ||
3251 | |||
3233 | return 0; | 3252 | return 0; |
3234 | } | 3253 | } |
3235 | 3254 | ||
@@ -3253,7 +3272,8 @@ int playlist_resume(void) | |||
3253 | splash(0, ID2P(LANG_WAIT)); | 3272 | splash(0, ID2P(LANG_WAIT)); |
3254 | 3273 | ||
3255 | struct playlist_info* playlist = ¤t_playlist; | 3274 | struct playlist_info* playlist = ¤t_playlist; |
3256 | playlist_mutex_lock(&(playlist->mutex)); | 3275 | dc_thread_stop(playlist); |
3276 | playlist_mutex_lock(&playlist->mutex); | ||
3257 | 3277 | ||
3258 | if (core_allocatable() < (1 << 10)) | 3278 | if (core_allocatable() < (1 << 10)) |
3259 | talk_buffer_set_policy(TALK_BUFFER_LOOSE); /* back off voice buffer */ | 3279 | talk_buffer_set_policy(TALK_BUFFER_LOOSE); /* back off voice buffer */ |
@@ -3437,8 +3457,7 @@ int playlist_resume(void) | |||
3437 | 3457 | ||
3438 | position = atoi(str1); | 3458 | position = atoi(str1); |
3439 | 3459 | ||
3440 | if (remove_track_from_playlist(playlist, position, | 3460 | if (remove_track_unlocked(playlist, position, false) < 0) |
3441 | false) < 0) | ||
3442 | { | 3461 | { |
3443 | result = -7; | 3462 | result = -7; |
3444 | goto out; | 3463 | goto out; |
@@ -3467,7 +3486,7 @@ int playlist_resume(void) | |||
3467 | seed = atoi(str1); | 3486 | seed = atoi(str1); |
3468 | playlist->first_index = atoi(str2); | 3487 | playlist->first_index = atoi(str2); |
3469 | 3488 | ||
3470 | if (randomise_playlist(playlist, seed, false, | 3489 | if (randomise_playlist_unlocked(playlist, seed, false, |
3471 | false) < 0) | 3490 | false) < 0) |
3472 | { | 3491 | { |
3473 | result = -9; | 3492 | result = -9; |
@@ -3647,10 +3666,9 @@ int playlist_resume(void) | |||
3647 | } | 3666 | } |
3648 | } | 3667 | } |
3649 | 3668 | ||
3650 | dc_load_playlist_pointers(); | ||
3651 | |||
3652 | out: | 3669 | out: |
3653 | playlist_mutex_unlock(&(playlist->mutex)); | 3670 | playlist_mutex_unlock(&playlist->mutex); |
3671 | dc_thread_start(playlist, true); | ||
3654 | 3672 | ||
3655 | talk_buffer_set_policy(TALK_BUFFER_DEFAULT); | 3673 | talk_buffer_set_policy(TALK_BUFFER_DEFAULT); |
3656 | core_free(handle); | 3674 | core_free(handle); |
@@ -3810,7 +3828,8 @@ int playlist_save(struct playlist_info* playlist, char *filename, | |||
3810 | { | 3828 | { |
3811 | strmemcpy(tmp_buf, path, pathlen); /* remove "_temp" */ | 3829 | strmemcpy(tmp_buf, path, pathlen); /* remove "_temp" */ |
3812 | 3830 | ||
3813 | playlist_mutex_lock(&(playlist->mutex)); | 3831 | dc_thread_stop(playlist); |
3832 | playlist_mutex_lock(&playlist->mutex); | ||
3814 | 3833 | ||
3815 | if (!rename(path, tmp_buf)) | 3834 | if (!rename(path, tmp_buf)) |
3816 | { | 3835 | { |
@@ -3822,7 +3841,6 @@ int playlist_save(struct playlist_info* playlist, char *filename, | |||
3822 | close(playlist->fd); | 3841 | close(playlist->fd); |
3823 | playlist->fd = fd; | 3842 | playlist->fd = fd; |
3824 | fd = -1; | 3843 | fd = -1; |
3825 | dc_discard_playlist_pointers(); | ||
3826 | 3844 | ||
3827 | if (!reparse) | 3845 | if (!reparse) |
3828 | { | 3846 | { |
@@ -3848,11 +3866,11 @@ int playlist_save(struct playlist_info* playlist, char *filename, | |||
3848 | /* we need to recreate control because inserted tracks are | 3866 | /* we need to recreate control because inserted tracks are |
3849 | now part of the playlist and shuffle has been invalidated */ | 3867 | now part of the playlist and shuffle has been invalidated */ |
3850 | result = recreate_control_unlocked(playlist); | 3868 | result = recreate_control_unlocked(playlist); |
3851 | dc_load_playlist_pointers(); | ||
3852 | } | 3869 | } |
3853 | } | 3870 | } |
3854 | 3871 | ||
3855 | playlist_mutex_unlock(&(playlist->mutex)); | 3872 | playlist_mutex_unlock(&playlist->mutex); |
3873 | dc_thread_start(playlist, true); | ||
3856 | } | 3874 | } |
3857 | 3875 | ||
3858 | if (fd >= 0) | 3876 | if (fd >= 0) |
@@ -3876,7 +3894,8 @@ int playlist_set_current(struct playlist_info* playlist) | |||
3876 | if (!playlist || (check_control(playlist) < 0)) | 3894 | if (!playlist || (check_control(playlist) < 0)) |
3877 | return result; | 3895 | return result; |
3878 | 3896 | ||
3879 | playlist_mutex_lock(&(current_playlist.mutex)); | 3897 | dc_thread_stop(¤t_playlist); |
3898 | playlist_mutex_lock(¤t_playlist.mutex); | ||
3880 | 3899 | ||
3881 | empty_playlist_unlocked(¤t_playlist, false); | 3900 | empty_playlist_unlocked(¤t_playlist, false); |
3882 | 3901 | ||
@@ -3927,7 +3946,8 @@ int playlist_set_current(struct playlist_info* playlist) | |||
3927 | result = 0; | 3946 | result = 0; |
3928 | 3947 | ||
3929 | out: | 3948 | out: |
3930 | playlist_mutex_unlock(&(current_playlist.mutex)); | 3949 | playlist_mutex_unlock(¤t_playlist.mutex); |
3950 | dc_thread_start(¤t_playlist, true); | ||
3931 | 3951 | ||
3932 | return result; | 3952 | return result; |
3933 | } | 3953 | } |
@@ -3946,9 +3966,11 @@ void playlist_set_last_shuffled_start(void) | |||
3946 | int playlist_shuffle(int random_seed, int start_index) | 3966 | int playlist_shuffle(int random_seed, int start_index) |
3947 | { | 3967 | { |
3948 | struct playlist_info* playlist = ¤t_playlist; | 3968 | struct playlist_info* playlist = ¤t_playlist; |
3949 | playlist_mutex_lock(&(playlist->mutex)); | ||
3950 | bool start_current = false; | 3969 | bool start_current = false; |
3951 | 3970 | ||
3971 | dc_thread_stop(playlist); | ||
3972 | playlist_mutex_lock(&(playlist->mutex)); | ||
3973 | |||
3952 | if (start_index >= 0 && global_settings.play_selected) | 3974 | if (start_index >= 0 && global_settings.play_selected) |
3953 | { | 3975 | { |
3954 | /* store the seek position before the shuffle */ | 3976 | /* store the seek position before the shuffle */ |
@@ -3956,8 +3978,11 @@ int playlist_shuffle(int random_seed, int start_index) | |||
3956 | start_current = true; | 3978 | start_current = true; |
3957 | } | 3979 | } |
3958 | 3980 | ||
3959 | randomise_playlist(playlist, random_seed, start_current, true); | 3981 | randomise_playlist_unlocked(playlist, random_seed, start_current, true); |
3982 | |||
3960 | playlist_mutex_unlock(&(playlist->mutex)); | 3983 | playlist_mutex_unlock(&(playlist->mutex)); |
3984 | dc_thread_start(playlist, true); | ||
3985 | |||
3961 | return playlist->index; | 3986 | return playlist->index; |
3962 | } | 3987 | } |
3963 | 3988 | ||
@@ -3993,7 +4018,8 @@ int playlist_sort(struct playlist_info* playlist, bool start_current) | |||
3993 | if (!playlist) | 4018 | if (!playlist) |
3994 | playlist = ¤t_playlist; | 4019 | playlist = ¤t_playlist; |
3995 | 4020 | ||
3996 | playlist_mutex_lock(&(playlist->mutex)); | 4021 | dc_thread_stop(playlist); |
4022 | playlist_mutex_lock(&playlist->mutex); | ||
3997 | 4023 | ||
3998 | check_control(playlist); | 4024 | check_control(playlist); |
3999 | result = sort_playlist_unlocked(playlist, start_current, true); | 4025 | result = sort_playlist_unlocked(playlist, start_current, true); |
@@ -4001,7 +4027,8 @@ int playlist_sort(struct playlist_info* playlist, bool start_current) | |||
4001 | if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) && playlist->started) | 4027 | if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) && playlist->started) |
4002 | audio_flush_and_reload_tracks(); | 4028 | audio_flush_and_reload_tracks(); |
4003 | 4029 | ||
4004 | playlist_mutex_unlock(&(playlist->mutex)); | 4030 | playlist_mutex_unlock(&playlist->mutex); |
4031 | dc_thread_start(playlist, true); | ||
4005 | return result; | 4032 | return result; |
4006 | } | 4033 | } |
4007 | 4034 | ||
@@ -4032,8 +4059,6 @@ void playlist_sync(struct playlist_info* playlist) | |||
4032 | sync_control(playlist, false); | 4059 | sync_control(playlist, false); |
4033 | if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started) | 4060 | if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started) |
4034 | audio_flush_and_reload_tracks(); | 4061 | audio_flush_and_reload_tracks(); |
4035 | |||
4036 | dc_load_playlist_pointers(); | ||
4037 | } | 4062 | } |
4038 | 4063 | ||
4039 | /* Update resume info for current playing song. Returns -1 on error. */ | 4064 | /* Update resume info for current playing song. Returns -1 on error. */ |