aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2008-09-23 22:37:18 +0200
committerMax Kellermann <max@duempel.org>2008-09-23 22:37:18 +0200
commit3f0ae13c4b409bcf39d926f5e73d5eb0feff6eb3 (patch)
treeb01aacd0c3c3ab7f7018830d630ada1d9d3838ae
parent0f0ac43b8fa4dfecad5548cde42152dba8d9a652 (diff)
downloadmpd-3f0ae13c4b409bcf39d926f5e73d5eb0feff6eb3.tar.gz
mpd-3f0ae13c4b409bcf39d926f5e73d5eb0feff6eb3.tar.xz
mpd-3f0ae13c4b409bcf39d926f5e73d5eb0feff6eb3.zip
directory: update do its work inside a thread
A lot of the preparation was needed (and done in previous months) in making update thread-safe, but here it is. This was the first thing I made work inside a thread when I started mpd-uclinux many years ago, and also the last thing I've done in mainline mpd to work inside a thread, go figure.
-rw-r--r--src/command.c47
-rw-r--r--src/directory.c156
-rw-r--r--src/directory.h2
-rw-r--r--src/main.c2
-rw-r--r--src/sig_handlers.c1
5 files changed, 58 insertions, 150 deletions
diff --git a/src/command.c b/src/command.c
index 2e45d6d25..0cd115cc5 100644
--- a/src/command.c
+++ b/src/command.c
@@ -819,13 +819,10 @@ static int listHandleUpdate(struct client *client,
char *argv[],
struct strnode *cmdnode, CommandEntry * cmd)
{
- static List *pathList;
+ List *pathList = makeList(NULL, 1);
CommandEntry *nextCmd = NULL;
struct strnode *next = cmdnode->next;
- if (!pathList)
- pathList = makeList(NULL, 1);
-
if (argc == 2)
insertInList(pathList, argv[1], NULL);
else
@@ -837,25 +834,9 @@ static int listHandleUpdate(struct client *client,
if (cmd != nextCmd) {
int ret = updateInit(pathList);
- freeList(pathList);
- pathList = NULL;
-
- switch (ret) {
- case 0:
+ if (ret == -1)
command_error(client, ACK_ERROR_UPDATE_ALREADY,
"already updating");
- break;
-
- case -1:
- command_error(client, ACK_ERROR_SYSTEM,
- "problems trying to update");
- break;
-
- default:
- client_printf(client, "updating_db: %i\n", ret);
- ret = 0;
- break;
- }
return ret;
}
@@ -872,26 +853,10 @@ static int handleUpdate(struct client *client,
List *pathList = makeList(NULL, 1);
insertInList(pathList, argv[1], NULL);
ret = updateInit(pathList);
- freeList(pathList);
- } else
- ret = updateInit(NULL);
-
- switch (ret) {
- case 0:
- command_error(client, ACK_ERROR_UPDATE_ALREADY,
- "already updating");
- ret = -1;
- break;
-
- case -1:
- command_error(client, ACK_ERROR_SYSTEM,
- "problems trying to update");
- break;
-
- default:
- client_printf(client, "updating_db: %i\n", ret);
- ret = 0;
- break;
+ if (ret == -1)
+ command_error(client, ACK_ERROR_UPDATE_ALREADY,
+ "already updating");
+ return ret;
}
return ret;
diff --git a/src/directory.c b/src/directory.c
index 3c0d08c07..52f9cb48b 100644
--- a/src/directory.c
+++ b/src/directory.c
@@ -53,17 +53,19 @@ enum update_return {
UPDATE_RETURN_UPDATED = 1
};
+enum update_progress {
+ UPDATE_PROGRESS_IDLE = 0,
+ UPDATE_PROGRESS_RUNNING = 1,
+ UPDATE_PROGRESS_DONE = 2
+} progress;
+
static Directory *mp3rootDirectory;
static time_t directory_dbModTime;
-static sig_atomic_t directory_updatePid;
-
-static sig_atomic_t update_exited;
-
-static sig_atomic_t update_status;
+static pthread_t update_thr;
-static sig_atomic_t directory_updateJobId;
+static int directory_updateJobId;
static DirectoryList *newDirectoryList(void);
@@ -116,124 +118,66 @@ static char *getDbFile(void)
int isUpdatingDB(void)
{
- return directory_updatePid > 0 ? directory_updateJobId : 0;
+ return (progress != UPDATE_PROGRESS_IDLE) ? directory_updateJobId : 0;
}
-void directory_sigChldHandler(int pid, int status)
+void reap_update_task(void)
{
- if (directory_updatePid == pid) {
- update_status = status;
- update_exited = 1;
- wakeup_main_task();
- }
+ if (progress != UPDATE_PROGRESS_DONE)
+ return;
+ pthread_join(update_thr, NULL);
+ progress = UPDATE_PROGRESS_IDLE;
}
-void readDirectoryDBIfUpdateIsFinished(void)
+static void * update_task(void *arg)
{
- int status;
-
- if (!update_exited)
- return;
+ List *path_list = (List *)arg;
+ enum update_return ret = UPDATE_RETURN_NOUPDATE;
- status = update_status;
-
- if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) {
- ERROR("update process died from a non-TERM signal: %d\n",
- WTERMSIG(status));
- } else if (!WIFSIGNALED(status)) {
- switch (WEXITSTATUS(status)) {
- case DIRECTORY_UPDATE_EXIT_UPDATE:
- DEBUG("update finished successfully with changes\n");
- readDirectoryDB();
- DEBUG("update changes read into memory\n");
- playlistVersionChange();
- case DIRECTORY_UPDATE_EXIT_NOUPDATE:
- DEBUG("update exited successfully with no changes\n");
- break;
- default:
- ERROR("error updating db\n");
+ if (path_list) {
+ ListNode *node = path_list->firstNode;
+
+ while (node) {
+ switch (updatePath(node->key)) {
+ case UPDATE_RETURN_ERROR:
+ ret = UPDATE_RETURN_ERROR;
+ goto out;
+ case UPDATE_RETURN_NOUPDATE:
+ break;
+ case UPDATE_RETURN_UPDATED:
+ ret = UPDATE_RETURN_UPDATED;
+ }
+ node = node->nextNode;
}
+ free(path_list);
+ } else {
+ ret = updateDirectory(mp3rootDirectory);
}
- update_exited = 0;
- directory_updatePid = 0;
+
+ if (ret == UPDATE_RETURN_UPDATED && writeDirectoryDB() < 0)
+ ret = UPDATE_RETURN_ERROR;
+out:
+ progress = UPDATE_PROGRESS_DONE;
+ wakeup_main_task();
+ return (void *)ret;
}
-int updateInit(List * pathList)
+int updateInit(List * path_list)
{
- if (directory_updatePid > 0)
- return 0;
+ pthread_attr_t attr;
- /*
- * need to block CHLD signal, cause it can exit before we
- * even get a chance to assign directory_updatePID
- *
- * Update: our signal blocking is is utterly broken by
- * pthreads(); goal will be to remove dependency on signals;
- * but for now use the my_usleep hack below.
- */
- blockSignals();
- directory_updatePid = fork();
- if (directory_updatePid == 0) {
- /* child */
- enum update_return dbUpdated = UPDATE_RETURN_NOUPDATE;
-
- unblockSignals();
-
- finishSigHandlers();
- closeAllListenSockets();
- client_manager_deinit();
- finishPlaylist();
- finishVolume();
-
- /*
- * XXX HACK to workaround race condition where
- * directory_updatePid is still zero in the parent even upon
- * entry of directory_sigChldHandler.
- */
- my_usleep(100000);
-
- if (pathList) {
- ListNode *node = pathList->firstNode;
-
- while (node) {
- switch (updatePath(node->key)) {
- case UPDATE_RETURN_UPDATED:
- dbUpdated = UPDATE_RETURN_UPDATED;
- break;
- case UPDATE_RETURN_NOUPDATE:
- break;
- case UPDATE_RETURN_ERROR:
- exit(DIRECTORY_UPDATE_EXIT_ERROR);
- }
- node = node->nextNode;
- }
- } else {
- dbUpdated = updateDirectory(mp3rootDirectory);
- if (dbUpdated == UPDATE_RETURN_ERROR)
- exit(DIRECTORY_UPDATE_EXIT_ERROR);
- }
-
- if (dbUpdated == UPDATE_RETURN_NOUPDATE)
- exit(DIRECTORY_UPDATE_EXIT_NOUPDATE);
-
- /* ignore signals since we don't want them to corrupt the db */
- ignoreSignals();
- if (writeDirectoryDB() < 0) {
- exit(DIRECTORY_UPDATE_EXIT_ERROR);
- }
- exit(DIRECTORY_UPDATE_EXIT_UPDATE);
- } else if (directory_updatePid < 0) {
- unblockSignals();
- ERROR("updateInit: Problems forking()'ing\n");
- directory_updatePid = 0;
+ if (progress != UPDATE_PROGRESS_IDLE)
return -1;
- }
- unblockSignals();
+ progress = UPDATE_PROGRESS_RUNNING;
+
+ pthread_attr_init(&attr);
+ if (pthread_create(&update_thr, &attr, update_task, path_list))
+ FATAL("Failed to spawn update task: %s\n", strerror(errno));
directory_updateJobId++;
if (directory_updateJobId > 1 << 15)
directory_updateJobId = 1;
- DEBUG("updateInit: fork()'d update child for update job id %i\n",
+ DEBUG("updateInit: spawned update thread for update job id %i\n",
(int)directory_updateJobId);
return (int)directory_updateJobId;
diff --git a/src/directory.h b/src/directory.h
index ccd20501e..6a8ca733f 100644
--- a/src/directory.h
+++ b/src/directory.h
@@ -34,7 +34,7 @@ typedef struct _Directory {
unsigned stat; /* not needed if ino_t == dev_t == 0 is impossible */
} Directory;
-void readDirectoryDBIfUpdateIsFinished(void);
+void reap_update_task(void);
int isUpdatingDB(void);
diff --git a/src/main.c b/src/main.c
index f37fa3328..3cfd75b3a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -444,7 +444,7 @@ int main(int argc, char *argv[])
COMMAND_RETURN_KILL != handlePendingSignals()) {
syncPlayerAndPlaylist();
client_manager_expire();
- readDirectoryDBIfUpdateIsFinished();
+ reap_update_task();
}
write_state_file();
diff --git a/src/sig_handlers.c b/src/sig_handlers.c
index 6b28cb675..e4ac21f22 100644
--- a/src/sig_handlers.c
+++ b/src/sig_handlers.c
@@ -57,7 +57,6 @@ static void chldSigHandler(mpd_unused int sig)
else
break;
}
- directory_sigChldHandler(pid, status);
}
}