workaround race condition on updates with broken signal blocking

pthreads with our existing signal blocking/handling is broken,
for now just sleep a bit in the child to prevent the CHLD handler
from being called too early.  Also, improve error reporting when
handling SIGCHLD by storing the status to be called in the main
task (which can be logged, since we can't do logging inside the
sig handler).
This commit is contained in:
Eric Wong 2008-09-23 20:48:44 +02:00 committed by Max Kellermann
parent 0bec1d3807
commit cce2f93de7
1 changed files with 50 additions and 39 deletions

View File

@ -32,6 +32,7 @@
#include "dbUtils.h"
#include "song_print.h"
#include "song_save.h"
#include "main_notify.h"
#define DIRECTORY_DIR "directory: "
#define DIRECTORY_MTIME "mtime: "
@ -54,11 +55,13 @@ static Directory *mp3rootDirectory;
static time_t directory_dbModTime;
static volatile int directory_updatePid;
static sig_atomic_t directory_updatePid;
static volatile int directory_reReadDB;
static sig_atomic_t update_exited;
static volatile mpd_uint16 directory_updateJobId;
static sig_atomic_t update_status;
static sig_atomic_t directory_updateJobId;
static DirectoryList *newDirectoryList(void);
@ -108,53 +111,48 @@ static char *getDbFile(void)
return param->value;
}
static void clearUpdatePid(void)
{
directory_updatePid = 0;
}
int isUpdatingDB(void)
{
if (directory_updatePid > 0 || directory_reReadDB) {
return directory_updateJobId;
}
return 0;
return directory_updatePid > 0 ? directory_updateJobId : 0;
}
void directory_sigChldHandler(int pid, int status)
{
if (directory_updatePid == pid) {
if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) {
/* ERROR("update process died from a "
"non-TERM signal: %i\n", WTERMSIG(status)); */
} else if (!WIFSIGNALED(status)) {
switch (WEXITSTATUS(status)) {
case DIRECTORY_UPDATE_EXIT_UPDATE:
directory_reReadDB = 1;
/* DEBUG("directory_sigChldHandler: "
"updated db\n"); */
case DIRECTORY_UPDATE_EXIT_NOUPDATE:
/* DEBUG("directory_sigChldHandler: "
"update exited succesffully\n"); */
break;
/*
default:
ERROR("error updating db\n");
*/
}
}
clearUpdatePid();
update_status = status;
update_exited = 1;
wakeup_main_task();
}
}
void readDirectoryDBIfUpdateIsFinished(void)
{
if (directory_reReadDB && 0 == directory_updatePid) {
DEBUG("readDirectoryDB since update finished successfully\n");
readDirectoryDB();
playlistVersionChange();
directory_reReadDB = 0;
int status;
if (!update_exited)
return;
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");
}
}
update_exited = 0;
directory_updatePid = 0;
}
int updateInit(List * pathList)
@ -162,8 +160,14 @@ int updateInit(List * pathList)
if (directory_updatePid > 0)
return 0;
/* need to block CHLD signal, cause it can exit before we
even get a chance to assign directory_updatePID */
/*
* 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) {
@ -178,6 +182,13 @@ int updateInit(List * pathList)
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;