Merge pull request #268 from chme/threadexit

Refactor thread exit handling
This commit is contained in:
ejurgensen 2016-06-11 22:57:09 +02:00 committed by GitHub
commit 547783d008
8 changed files with 71 additions and 341 deletions

View File

@ -72,8 +72,6 @@ static pthread_t tid_cache;
// Event base, pipes and events
struct event_base *evbase_cache;
static int g_exit_pipe[2];
static struct event *g_exitev;
static struct event *g_cacheev;
static struct commands_base *cmdbase;
@ -123,18 +121,6 @@ remove_tag(char *in, const char *tag)
}
static void
thread_exit(void)
{
int dummy = 42;
DPRINTF(E_DBG, L_CACHE, "Killing cache thread\n");
if (write(g_exit_pipe[1], &dummy, sizeof(dummy)) != sizeof(dummy))
DPRINTF(E_LOG, L_CACHE, "Could not write to exit fd: %s\n", strerror(errno));
}
/* --------------------------------- MAIN --------------------------------- */
/* Thread: cache */
@ -1316,23 +1302,6 @@ cache(void *arg)
pthread_exit(NULL);
}
static void
exit_cb(int fd, short what, void *arg)
{
int dummy;
int ret;
ret = read(g_exit_pipe[0], &dummy, sizeof(dummy));
if (ret != sizeof(dummy))
DPRINTF(E_LOG, L_CACHE, "Error reading from exit pipe\n");
event_base_loopbreak(evbase_cache);
g_initialized = 0;
event_add(g_exitev, NULL);
}
/* ---------------------------- DAAP cache API --------------------------- */
@ -1637,17 +1606,6 @@ cache_init(void)
return 0;
}
#ifdef HAVE_PIPE2
ret = pipe2(g_exit_pipe, O_CLOEXEC);
#else
ret = pipe(g_exit_pipe);
#endif
if (ret < 0)
{
DPRINTF(E_LOG, L_CACHE, "Could not create exit pipe: %s\n", strerror(errno));
goto exit_fail;
}
evbase_cache = event_base_new();
if (!evbase_cache)
{
@ -1655,13 +1613,6 @@ cache_init(void)
goto evbase_fail;
}
g_exitev = event_new(evbase_cache, g_exit_pipe[0], EV_READ, exit_cb, NULL);
if (!g_exitev)
{
DPRINTF(E_LOG, L_CACHE, "Could not create exit event\n");
goto evnew_fail;
}
g_cacheev = evtimer_new(evbase_cache, cache_daap_update_cb, NULL);
if (!g_cacheev)
{
@ -1669,9 +1620,7 @@ cache_init(void)
goto evnew_fail;
}
event_add(g_exitev, NULL);
cmdbase = commands_base_new(evbase_cache);
cmdbase = commands_base_new(evbase_cache, NULL);
DPRINTF(E_INFO, L_CACHE, "cache thread init\n");
@ -1698,10 +1647,6 @@ cache_init(void)
evbase_cache = NULL;
evbase_fail:
close(g_exit_pipe[0]);
close(g_exit_pipe[1]);
exit_fail:
return -1;
}
@ -1713,7 +1658,8 @@ cache_deinit(void)
if (!g_initialized)
return;
thread_exit();
g_initialized = 0;
commands_base_destroy(cmdbase);
ret = pthread_join(tid_cache, NULL);
if (ret != 0)
@ -1724,9 +1670,4 @@ cache_deinit(void)
// Free event base (should free events too)
event_base_free(evbase_cache);
// Close pipes and free command base
commands_base_free(cmdbase);
close(g_exit_pipe[0]);
close(g_exit_pipe[1]);
}

View File

@ -46,6 +46,8 @@ struct command
struct commands_base
{
struct event_base *evbase;
command_exit_cb exit_cb;
int command_pipe[2];
struct event *command_event;
struct command *current_cmd;
@ -166,10 +168,13 @@ send_command(struct commands_base *cmdbase, struct command *cmd)
}
/*
* Creates a new command base, needs to be freed by commands_base_free.
* Creates a new command base, needs to be freed by commands_base_destroy or commands_base_free.
*
* @param evbase The libevent base to use for command handling
* @param exit_cb Callback function to be called during commands_base_destroy
*/
struct commands_base *
commands_base_new(struct event_base *evbase)
commands_base_new(struct event_base *evbase, command_exit_cb exit_cb)
{
struct commands_base *cmdbase;
int ret;
@ -213,6 +218,9 @@ commands_base_new(struct event_base *evbase)
return NULL;
}
cmdbase->evbase = evbase;
cmdbase->exit_cb = exit_cb;
return cmdbase;
}
@ -359,3 +367,39 @@ commands_exec_async(struct commands_base *cmdbase, command_function func, void *
return 0;
}
/*
* Command to break the libevent loop
*
* If the command base was created with an exit_cb function, exit_cb is called before breaking the
* libevent loop.
*
* @param arg The command base
* @param retval Always set to COMMAND_END
*/
static enum command_state
cmdloop_exit(void *arg, int *retval)
{
struct commands_base *cmdbase = arg;
*retval = 0;
if (cmdbase->exit_cb)
cmdbase->exit_cb();
event_base_loopbreak(cmdbase->evbase);
return COMMAND_END;
}
/*
* Break the libevent loop for the given command base, closes the internally used pipes
* and frees the command base.
*
* @param cmdbase The command base
*/
void
commands_base_destroy(struct commands_base *cmdbase)
{
commands_exec_sync(cmdbase, cmdloop_exit, NULL, cmdbase);
commands_base_free(cmdbase);
}

View File

@ -39,11 +39,14 @@ enum command_state {
*/
typedef enum command_state (*command_function)(void *arg, int *ret);
typedef void (*command_exit_cb)(void);
struct commands_base;
struct commands_base *
commands_base_new(struct event_base *evbase);
commands_base_new(struct event_base *evbase, command_exit_cb exit_cb);
int
commands_base_free(struct commands_base *cmdbase);
@ -60,5 +63,7 @@ commands_exec_sync(struct commands_base *cmdbase, command_function func, command
int
commands_exec_async(struct commands_base *cmdbase, command_function func, void *arg);
void
commands_base_destroy(struct commands_base *cmdbase);
#endif /* SRC_COMMANDS_H_ */

View File

@ -104,12 +104,10 @@ struct stacked_dir {
struct stacked_dir *next;
};
static int exit_pipe[2];
static int scan_exit;
static int inofd;
static struct event_base *evbase_scan;
static struct event *inoev;
static struct event *exitev;
static pthread_t tid_scan;
static struct deferred_pl *playlists;
static struct stacked_dir *dirstack;
@ -1911,13 +1909,6 @@ inofd_event_unset(void)
}
/* Thread: scan */
static void
exit_cb(int fd, short event, void *arg)
{
event_base_loopbreak(evbase_scan);
scan_exit = 1;
}
static enum command_state
filescanner_initscan(void *arg, int *retval)
@ -2002,32 +1993,13 @@ filescanner_init(void)
return -1;
}
#ifdef HAVE_PIPE2
ret = pipe2(exit_pipe, O_CLOEXEC);
#else
ret = pipe(exit_pipe);
#endif
if (ret < 0)
{
DPRINTF(E_FATAL, L_SCAN, "Could not create pipe: %s\n", strerror(errno));
goto pipe_fail;
}
exitev = event_new(evbase_scan, exit_pipe[0], EV_READ, exit_cb, NULL);
if (!exitev || (event_add(exitev, NULL) < 0))
{
DPRINTF(E_LOG, L_SCAN, "Could not create/add command event\n");
goto exitev_fail;
}
ret = inofd_event_set();
if (ret < 0)
{
goto ino_fail;
}
cmdbase = commands_base_new(evbase_scan);
cmdbase = commands_base_new(evbase_scan, NULL);
ret = pthread_create(&tid_scan, NULL, filescanner, NULL);
if (ret != 0)
@ -2048,11 +2020,7 @@ filescanner_init(void)
thread_fail:
commands_base_free(cmdbase);
close(inofd);
exitev_fail:
ino_fail:
close(exit_pipe[0]);
close(exit_pipe[1]);
pipe_fail:
event_base_free(evbase_scan);
return -1;
@ -2063,17 +2031,9 @@ void
filescanner_deinit(void)
{
int ret;
int dummy = 42;
ret = write(exit_pipe[1], &dummy, sizeof(dummy));
if (ret != sizeof(dummy))
{
DPRINTF(E_FATAL, L_SCAN, "Could not write to exit fd: %s\n", strerror(errno));
return;
}
scan_exit = 1;
commands_base_destroy(cmdbase);
ret = pthread_join(tid_scan, NULL);
if (ret != 0)
@ -2086,7 +2046,4 @@ filescanner_deinit(void)
inofd_event_unset();
event_base_free(evbase_scan);
commands_base_free(cmdbase);
close(exit_pipe[0]);
close(exit_pipe[1]);
}

View File

@ -68,8 +68,6 @@
static pthread_t tid_mpd;
static struct event_base *evbase_mpd;
static int g_exit_pipe[2];
static struct event *g_exitev;
static struct commands_base *cmdbase;
@ -192,17 +190,6 @@ struct idle_client
struct idle_client *idle_clients;
static void
thread_exit(void)
{
int dummy = 42;
DPRINTF(E_DBG, L_MPD, "Killing mpd thread\n");
if (write(g_exit_pipe[1], &dummy, sizeof(dummy)) != sizeof(dummy))
DPRINTF(E_LOG, L_MPD, "Could not write to exit fd: %s\n", strerror(errno));
}
/* Thread: mpd */
static void *
@ -225,21 +212,6 @@ mpd(void *arg)
pthread_exit(NULL);
}
static void
exit_cb(int fd, short what, void *arg)
{
int dummy;
int ret;
ret = read(g_exit_pipe[0], &dummy, sizeof(dummy));
if (ret != sizeof(dummy))
DPRINTF(E_LOG, L_MPD, "Error reading from exit pipe\n");
event_base_loopbreak(evbase_mpd);
event_add(g_exitev, NULL);
}
static void
mpd_time(char *buffer, size_t bufferlen, time_t t)
{
@ -4670,17 +4642,6 @@ int mpd_init(void)
v6enabled = cfg_getbool(cfg_getsec(cfg, "general"), "ipv6");
#ifdef HAVE_PIPE2
ret = pipe2(g_exit_pipe, O_CLOEXEC);
#else
ret = pipe(g_exit_pipe);
#endif
if (ret < 0)
{
DPRINTF(E_LOG, L_MPD, "Could not create pipe: %s\n", strerror(errno));
goto exit_fail;
}
evbase_mpd = event_base_new();
if (!evbase_mpd)
{
@ -4688,16 +4649,7 @@ int mpd_init(void)
goto evbase_fail;
}
g_exitev = event_new(evbase_mpd, g_exit_pipe[0], EV_READ, exit_cb, NULL);
if (!g_exitev)
{
DPRINTF(E_LOG, L_MPD, "Could not create exit event\n");
goto evnew_fail;
}
event_add(g_exitev, NULL);
cmdbase = commands_base_new(evbase_mpd);
cmdbase = commands_base_new(evbase_mpd, NULL);
if (v6enabled)
{
@ -4808,15 +4760,10 @@ int mpd_init(void)
evconnlistener_free(listener);
connew_fail:
commands_base_free(cmdbase);
evnew_fail:
event_base_free(evbase_mpd);
evbase_mpd = NULL;
evbase_fail:
close(g_exit_pipe[0]);
close(g_exit_pipe[1]);
exit_fail:
return -1;
}
@ -4835,7 +4782,7 @@ void mpd_deinit(void)
return;
}
thread_exit();
commands_base_destroy(cmdbase);
ret = pthread_join(tid_mpd, NULL);
if (ret != 0)
@ -4861,9 +4808,4 @@ void mpd_deinit(void)
// Free event base (should free events too)
event_base_free(evbase_mpd);
// Close pipes and free command base
commands_base_free(cmdbase);
close(g_exit_pipe[0]);
close(g_exit_pipe[1]);
}

View File

@ -220,9 +220,7 @@ union player_arg
struct event_base *evbase_player;
static int exit_pipe[2];
static int player_exit;
static struct event *exitev;
static pthread_t tid_player;
static struct commands_base *cmdbase;
@ -3952,14 +3950,6 @@ player(void *arg)
pthread_exit(NULL);
}
/* Thread: player */
static void
exit_cb(int fd, short what, void *arg)
{
event_base_loopbreak(evbase_player);
player_exit = 1;
}
/* Thread: main */
int
@ -4041,18 +4031,6 @@ player_init(void)
goto audio_fail;
}
#ifdef HAVE_PIPE2
ret = pipe2(exit_pipe, O_CLOEXEC);
#else
ret = pipe(exit_pipe);
#endif
if (ret < 0)
{
DPRINTF(E_LOG, L_PLAYER, "Could not create pipe: %s\n", strerror(errno));
goto exit_fail;
}
evbase_player = event_base_new();
if (!evbase_player)
{
@ -4061,13 +4039,6 @@ player_init(void)
goto evbase_fail;
}
exitev = event_new(evbase_player, exit_pipe[0], EV_READ, exit_cb, NULL);
if (!exitev)
{
DPRINTF(E_LOG, L_PLAYER, "Could not create exit event\n");
goto evnew_fail;
}
#if defined(__linux__)
pb_timer_ev = event_new(evbase_player, pb_timer_fd, EV_READ | EV_PERSIST, player_playback_cb, NULL);
#else
@ -4079,10 +4050,9 @@ player_init(void)
goto evnew_fail;
}
event_add(exitev, NULL);
event_add(pb_timer_ev, NULL);
cmdbase = commands_base_new(evbase_player);
cmdbase = commands_base_new(evbase_player, NULL);
ret = outputs_init();
if (ret < 0)
@ -4112,9 +4082,6 @@ player_init(void)
evnew_fail:
event_base_free(evbase_player);
evbase_fail:
close(exit_pipe[0]);
close(exit_pipe[1]);
exit_fail:
evbuffer_free(audio_buf);
audio_fail:
#if defined(__linux__)
@ -4131,20 +4098,14 @@ void
player_deinit(void)
{
int ret;
int dummy = 42;
ret = write(exit_pipe[1], &dummy, sizeof(dummy));
if (ret != sizeof(dummy))
{
DPRINTF(E_LOG, L_PLAYER, "Could not write to exit pipe: %s\n", strerror(errno));
return;
}
player_exit = 1;
commands_base_destroy(cmdbase);
ret = pthread_join(tid_player, NULL);
if (ret != 0)
{
DPRINTF(E_LOG, L_PLAYER, "Could not join HTTPd thread: %s\n", strerror(errno));
DPRINTF(E_LOG, L_PLAYER, "Could not join player thread: %s\n", strerror(errno));
return;
}
@ -4164,8 +4125,4 @@ player_deinit(void)
outputs_deinit();
event_base_free(evbase_player);
commands_base_free(cmdbase);
close(exit_pipe[0]);
close(exit_pipe[1]);
}

View File

@ -114,9 +114,7 @@ static pthread_cond_t login_cond;
// Event base, pipes and events
struct event_base *evbase_spotify;
static int g_exit_pipe[2];
static int g_notify_pipe[2];
static struct event *g_exitev;
static struct event *g_notifyev;
static struct commands_base *cmdbase;
@ -384,21 +382,6 @@ fptr_assign_all()
// End of ugly part
/* ---------------------------- COMMAND EXECUTION -------------------------- */
/* Thread: main and filescanner */
static void
thread_exit(void)
{
int dummy = 42;
DPRINTF(E_DBG, L_SPOTIFY, "Killing Spotify thread\n");
if (write(g_exit_pipe[1], &dummy, sizeof(dummy)) != sizeof(dummy))
DPRINTF(E_LOG, L_SPOTIFY, "Could not write to exit fd: %s\n", strerror(errno));
}
/* -------------------------- PLAYLIST HELPERS ------------------------- */
/* Should only be called from within the spotify thread */
@ -1619,23 +1602,11 @@ spotify(void *arg)
}
static void
exit_cb(int fd, short what, void *arg)
exit_cb()
{
int dummy;
int ret;
ret = read(g_exit_pipe[0], &dummy, sizeof(dummy));
if (ret != sizeof(dummy))
DPRINTF(E_LOG, L_SPOTIFY, "Error reading from exit pipe\n");
fptr_sp_session_player_unload(g_sess);
fptr_sp_session_logout(g_sess);
event_base_loopbreak(evbase_spotify);
g_state = SPOTIFY_STATE_INACTIVE;
event_add(g_exitev, NULL);
}
/* Process events when timeout expires or triggered by libspotify's notify_main_thread */
@ -1976,17 +1947,6 @@ spotify_init(void)
if (ret < 0)
goto assign_fail;
#ifdef HAVE_PIPE2
ret = pipe2(g_exit_pipe, O_CLOEXEC);
#else
ret = pipe(g_exit_pipe);
#endif
if (ret < 0)
{
DPRINTF(E_LOG, L_SPOTIFY, "Could not create pipe: %s\n", strerror(errno));
goto exit_fail;
}
#ifdef HAVE_PIPE2
ret = pipe2(g_notify_pipe, O_CLOEXEC);
#else
@ -2005,13 +1965,6 @@ spotify_init(void)
goto evbase_fail;
}
g_exitev = event_new(evbase_spotify, g_exit_pipe[0], EV_READ, exit_cb, NULL);
if (!g_exitev)
{
DPRINTF(E_LOG, L_SPOTIFY, "Could not create exit event\n");
goto evnew_fail;
}
g_notifyev = event_new(evbase_spotify, g_notify_pipe[0], EV_READ | EV_TIMEOUT, notify_cb, NULL);
if (!g_notifyev)
{
@ -2019,11 +1972,10 @@ spotify_init(void)
goto evnew_fail;
}
event_add(g_exitev, NULL);
event_add(g_notifyev, NULL);
cmdbase = commands_base_new(evbase_spotify);
cmdbase = commands_base_new(evbase_spotify, exit_cb);
if (!cmdbase)
{
DPRINTF(E_LOG, L_SPOTIFY, "Could not create command base\n");
@ -2116,10 +2068,6 @@ spotify_init(void)
close(g_notify_pipe[1]);
notify_fail:
close(g_exit_pipe[0]);
close(g_exit_pipe[1]);
exit_fail:
assign_fail:
dlclose(g_libhandle);
g_libhandle = NULL;
@ -2139,7 +2087,8 @@ spotify_deinit(void)
/* Send exit signal to thread (if active) */
if (g_state != SPOTIFY_STATE_INACTIVE)
{
thread_exit();
commands_base_destroy(cmdbase);
g_state = SPOTIFY_STATE_INACTIVE;
ret = pthread_join(tid_spotify, NULL);
if (ret != 0)
@ -2155,12 +2104,9 @@ spotify_deinit(void)
/* Free event base (should free events too) */
event_base_free(evbase_spotify);
/* Close pipes and free command base */
commands_base_free(cmdbase);
/* Close pipes */
close(g_notify_pipe[0]);
close(g_notify_pipe[1]);
close(g_exit_pipe[0]);
close(g_exit_pipe[1]);
/* Destroy locks */
pthread_cond_destroy(&login_cond);

View File

@ -58,8 +58,6 @@ static pthread_t tid_worker;
// Event base, pipes and events
struct event_base *evbase_worker;
static int g_initialized;
static int g_exit_pipe[2];
static struct event *g_exitev;
static struct commands_base *cmdbase;
@ -102,20 +100,6 @@ execute(void *arg, int *retval)
}
/* Thread: main */
static void
thread_exit(void)
{
int dummy = 42;
DPRINTF(E_DBG, L_MAIN, "Killing worker thread\n");
if (write(g_exit_pipe[1], &dummy, sizeof(dummy)) != sizeof(dummy))
DPRINTF(E_LOG, L_MAIN, "Could not write to exit fd: %s\n", strerror(errno));
}
/* --------------------------------- MAIN --------------------------------- */
/* Thread: worker */
@ -146,23 +130,6 @@ worker(void *arg)
pthread_exit(NULL);
}
static void
exit_cb(int fd, short what, void *arg)
{
int dummy;
int ret;
ret = read(g_exit_pipe[0], &dummy, sizeof(dummy));
if (ret != sizeof(dummy))
DPRINTF(E_LOG, L_MAIN, "Error reading from exit pipe\n");
event_base_loopbreak(evbase_worker);
g_initialized = 0;
event_add(g_exitev, NULL);
}
/* ---------------------------- Our worker API --------------------------- */
@ -205,17 +172,6 @@ worker_init(void)
{
int ret;
#ifdef HAVE_PIPE2
ret = pipe2(g_exit_pipe, O_CLOEXEC);
#else
ret = pipe(g_exit_pipe);
#endif
if (ret < 0)
{
DPRINTF(E_LOG, L_MAIN, "Could not create pipe: %s\n", strerror(errno));
goto exit_fail;
}
evbase_worker = event_base_new();
if (!evbase_worker)
{
@ -223,16 +179,7 @@ worker_init(void)
goto evbase_fail;
}
g_exitev = event_new(evbase_worker, g_exit_pipe[0], EV_READ, exit_cb, NULL);
if (!g_exitev)
{
DPRINTF(E_LOG, L_MAIN, "Could not create exit event\n");
goto evnew_fail;
}
cmdbase = commands_base_new(evbase_worker);
event_add(g_exitev, NULL);
cmdbase = commands_base_new(evbase_worker, NULL);
ret = pthread_create(&tid_worker, NULL, worker, NULL);
if (ret < 0)
@ -252,15 +199,10 @@ worker_init(void)
thread_fail:
commands_base_free(cmdbase);
evnew_fail:
event_base_free(evbase_worker);
evbase_worker = NULL;
evbase_fail:
close(g_exit_pipe[0]);
close(g_exit_pipe[1]);
exit_fail:
return -1;
}
@ -269,7 +211,8 @@ worker_deinit(void)
{
int ret;
thread_exit();
g_initialized = 0;
commands_base_destroy(cmdbase);
ret = pthread_join(tid_worker, NULL);
if (ret != 0)
@ -280,9 +223,4 @@ worker_deinit(void)
// Free event base (should free events too)
event_base_free(evbase_worker);
// Close pipes and free command base
commands_base_free(cmdbase);
close(g_exit_pipe[0]);
close(g_exit_pipe[1]);
}