diff options
-rw-r--r-- | DOCS/man/input.rst | 3 | ||||
-rw-r--r-- | TOOLS/lua/command-test.lua | 16 | ||||
-rw-r--r-- | player/client.c | 54 | ||||
-rw-r--r-- | player/core.h | 1 | ||||
-rw-r--r-- | player/loadfile.c | 5 |
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, |