summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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,