summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2018-05-13 13:48:47 +0200
committerwm4 <wm4@nowhere>2018-05-24 19:56:34 +0200
commit7428cc51496ca8e56600fdc4034b8f55720f09f9 (patch)
tree4e09e1818b2e60ccf1db3b1ba04cb227275a658f
parent4e05f75261d997cfc48227f5ac1ab8f18101c437 (diff)
downloadmpv-7428cc51496ca8e56600fdc4034b8f55720f09f9.tar.bz2
mpv-7428cc51496ca8e56600fdc4034b8f55720f09f9.tar.xz
client API: kill async commands on termination
This affects async commands started by client API, commands with async capability run in a sync way by client API (think mpv_command_node() with "subprocess"), and detached async work. Since scripts might want to do some cleanup work (that might involve launching processes, don't ask), we don't unconditionally kill everything on exit, but apply an arbitrary timeout of 2 seconds until async commands are aborted.
-rw-r--r--DOCS/man/input.rst3
-rw-r--r--TOOLS/lua/command-test.lua16
-rw-r--r--player/client.c54
-rw-r--r--player/core.h1
-rw-r--r--player/loadfile.c5
5 files changed, 65 insertions, 14 deletions
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst
index cadf515567..4b5ebffd0f 100644
--- a/DOCS/man/input.rst
+++ b/DOCS/man/input.rst
@@ -365,6 +365,9 @@ List of Input Commands
This command can be asynchronously aborted via API.
+ In all cases, the subprocess will be terminated on player exit. Only the
+ ``run`` command can start processes in a truly detached way.
+
``quit [<code>]``
Exit the player. If an argument is given, it's used as process exit code.
diff --git a/TOOLS/lua/command-test.lua b/TOOLS/lua/command-test.lua
index 1a8d86b4dd..79c1127cbf 100644
--- a/TOOLS/lua/command-test.lua
+++ b/TOOLS/lua/command-test.lua
@@ -64,4 +64,20 @@ mp.observe_property("vo-configured", "bool", function(_, v)
print("aborting sleep inf subprocess after timeout")
mp.abort_async_command(x)
end)
+
+ -- This should get killed on script exit.
+ mp.command_native_async({name = "subprocess", playback_only = false,
+ args = {"sleep", "inf"}}, function()end)
+
+ -- Runs detached; should be killed on player exit (forces timeout)
+ mp.command_native({_flags={"async"}, name = "subprocess",
+ playback_only = false, args = {"sleep", "inf"}})
+end)
+
+mp.register_event("shutdown", function()
+ -- This "freezes" the script, should be killed via timeout.
+ print("freeze!")
+ local x = mp.command_native({name = "subprocess", playback_only = false,
+ args = {"sleep", "inf"}})
+ print("done, killed=" .. utils.to_string(x.killed_by_us))
end)
diff --git a/player/client.c b/player/client.c
index 31339f47a3..06fb88c18b 100644
--- a/player/client.c
+++ b/player/client.c
@@ -371,6 +371,30 @@ void mpv_wait_async_requests(mpv_handle *ctx)
pthread_mutex_unlock(&ctx->lock);
}
+// Send abort signal to all matching work items.
+// If type==0, destroy all of the matching ctx.
+// If ctx==0, destroy all.
+static void abort_async(struct MPContext *mpctx, mpv_handle *ctx,
+ int type, uint64_t id)
+{
+ pthread_mutex_lock(&mpctx->abort_lock);
+
+ // Destroy all => ensure any newly appearing work is aborted immediately.
+ if (ctx == NULL)
+ mpctx->abort_all = true;
+
+ for (int n = 0; n < mpctx->num_abort_list; n++) {
+ struct mp_abort_entry *abort = mpctx->abort_list[n];
+ if (!ctx || (abort->client == ctx && (!type ||
+ (abort->client_work_type == type && abort->client_work_id == id))))
+ {
+ mp_abort_trigger_locked(mpctx, abort);
+ }
+ }
+
+ pthread_mutex_unlock(&mpctx->abort_lock);
+}
+
static void get_thread(void *ptr)
{
*(pthread_t *)ptr = pthread_self();
@@ -389,6 +413,8 @@ static void mp_destroy_client(mpv_handle *ctx, bool terminate)
if (terminate)
mpv_command(ctx, (const char*[]){"quit", NULL});
+ abort_async(mpctx, ctx, 0, 0);
+
// reserved_events equals the number of asynchronous requests that weren't
// yet replied. In order to avoid that trying to reply to a removed client
// causes a crash, block until all asynchronous requests were served.
@@ -485,10 +511,14 @@ void mpv_terminate_destroy(mpv_handle *ctx)
}
// Can be called on the core thread only. Idempotent.
+// Also happens to take care of shutting down any async work.
void mp_shutdown_clients(struct MPContext *mpctx)
{
struct mp_client_api *clients = mpctx->clients;
+ // Forcefully abort async work after 2 seconds of waiting.
+ double abort_time = mp_time_sec() + 2;
+
pthread_mutex_lock(&clients->lock);
// Prevent that new clients can appear.
@@ -500,6 +530,16 @@ void mp_shutdown_clients(struct MPContext *mpctx)
{
pthread_mutex_unlock(&clients->lock);
+ double left = abort_time - mp_time_sec();
+ if (left >= 0) {
+ mp_set_timeout(mpctx, left);
+ } else {
+ // Forcefully abort any ongoing async work. This is quite rude and
+ // probably not what everyone wants, so it happens only after a
+ // timeout.
+ abort_async(mpctx, NULL, 0, 0);
+ }
+
mp_client_broadcast_event(mpctx, MPV_EVENT_SHUTDOWN, NULL);
mp_wait_events(mpctx);
@@ -1178,19 +1218,7 @@ int mpv_command_node_async(mpv_handle *ctx, uint64_t ud, mpv_node *args)
void mpv_abort_async_command(mpv_handle *ctx, uint64_t reply_userdata)
{
- struct MPContext *mpctx = ctx->mpctx;
-
- pthread_mutex_lock(&mpctx->abort_lock);
- for (int n = 0; n < mpctx->num_abort_list; n++) {
- struct mp_abort_entry *abort = mpctx->abort_list[n];
- if (abort->client == ctx &&
- abort->client_work_type == MPV_EVENT_COMMAND_REPLY &&
- abort->client_work_id == reply_userdata)
- {
- mp_abort_trigger_locked(mpctx, abort);
- }
- }
- pthread_mutex_unlock(&mpctx->abort_lock);
+ abort_async(ctx->mpctx, ctx, MPV_EVENT_COMMAND_REPLY, reply_userdata);
}
static int translate_property_error(int errc)
diff --git a/player/core.h b/player/core.h
index a42b1252b8..b0d9b2a5ea 100644
--- a/player/core.h
+++ b/player/core.h
@@ -444,6 +444,7 @@ typedef struct MPContext {
struct mp_cancel *demuxer_cancel; // cancel handle for MPContext.demuxer
struct mp_abort_entry **abort_list;
int num_abort_list;
+ bool abort_all; // during final termination
// --- Owned by MPContext
pthread_t open_thread;
diff --git a/player/loadfile.c b/player/loadfile.c
index 6f28d2ee38..c4e013793d 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -113,8 +113,11 @@ void mp_abort_remove(struct MPContext *mpctx, struct mp_abort_entry *abort)
void mp_abort_recheck_locked(struct MPContext *mpctx,
struct mp_abort_entry *abort)
{
- if (abort->coupled_to_playback && mp_cancel_test(mpctx->playback_abort))
+ if ((abort->coupled_to_playback && mp_cancel_test(mpctx->playback_abort)) ||
+ mpctx->abort_all)
+ {
mp_abort_trigger_locked(mpctx, abort);
+ }
}
void mp_abort_trigger_locked(struct MPContext *mpctx,